/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the Apache License, Version 2.0, please send an email to 
 * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Apache License, Version 2.0.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * ***************************************************************************/

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Xml;
using EnvDTE;
using Microsoft.Build.Execution;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
using IServiceProvider = System.IServiceProvider;
using MSBuild = Microsoft.Build.Evaluation;
using MSBuildConstruction = Microsoft.Build.Construction;
using MSBuildExecution = Microsoft.Build.Execution;
using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants;
using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID;
using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID;
using System.Runtime.Versioning;
using System.ComponentModel.Design;
using System.Reflection;
using Microsoft.Build.Utilities;
using Microsoft.VisualStudio.ReferenceManager.Providers;
using Microsoft.Build.Evaluation;
using Microsoft.Win32;
using System.Collections;
using System.Runtime;

namespace Microsoft.VisualStudio.Project
{
    /// <summary>
    /// Manages the persistent state of the project (References, options, files, etc.) and deals with user interaction via a GUI in the form a hierarchy.
    /// </summary>

    public abstract partial class ProjectNode : HierarchyNode,
        IVsUIHierarchy,
        IVsPersistHierarchyItem2,
        IVsHierarchyDeleteHandler,
        IVsHierarchyDropDataTarget,
        IVsHierarchyDropDataSource,
        IVsHierarchyDropDataSource2,
        IVsGetCfgProvider,
        IVsProject3,
        IVsAggregatableProject,
        IVsProjectFlavorCfgProvider,
        IPersistFileFormat,
        IVsBuildPropertyStorage,
        IVsComponentUser,
        IVsDependencyProvider,
        IReferenceContainerProvider,
        IVsSccProject2,
        IBuildDependencyUpdate,
        IVsProjectSpecialFiles,
        IVsProjectBuildSystem,
        IOleCommandTarget
        //IVsSetTargetFrameworkWorkerCallback
    {
        #region nested types

        public enum ImageName
        {
            OfflineWebApp = 0,
            WebReferencesFolder = 1,
            OpenReferenceFolder = 2,
            ReferenceFolder = 3,
            Reference = 4,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SDL")]
            SDLWebReference = 5,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "DISCO")]
            DISCOWebReference = 6,
            Folder = 7,
            OpenFolder = 8,
            ExcludedFolder = 9,
            OpenExcludedFolder = 10,
            ExcludedFile = 11,
            DependentFile = 12,
            MissingFile = 13,
            WindowsForm = 14,
            WindowsUserControl = 15,
            WindowsComponent = 16,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")]
            XMLSchema = 17,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")]
            XMLFile = 18,
            WebForm = 19,
            WebService = 20,
            WebUserControl = 21,
            WebCustomUserControl = 22,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ASP")]
            ASPPage = 23,
            GlobalApplicationClass = 24,
            WebConfig = 25,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "HTML")]
            HTMLPage = 26,
            StyleSheet = 27,
            ScriptFile = 28,
            TextFile = 29,
            SettingsFile = 30,
            Resources = 31,
            Bitmap = 32,
            Icon = 33,
            Image = 34,
            ImageMap = 35,
            XWorld = 36,
            Audio = 37,
            Video = 38,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CAB")]
            CAB = 39,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "JAR")]
            JAR = 40,
            DataEnvironment = 41,
            PreviewFile = 42,
            DanglingReference = 43,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XSLT")]
            XSLTFile = 44,
            Cursor = 45,
            AppDesignerFolder = 46,
            Data = 47,
            Application = 48,
            DataSet = 49,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PFX")]
            PFX = 50,
            [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SNK")]
            SNK = 51,

            ImageLast = 51
        }

        /// <summary>
        /// Flags for specifying which events to stop triggering.
        /// </summary>
        [Flags]
        public enum EventTriggering
        {
            TriggerAll = 0,
            DoNotTriggerHierarchyEvents = 1,
            DoNotTriggerTrackerEvents = 2,
            DoNotTriggerTrackerQueryEvents = 4
        }

        private class HierarchyEventsSink : IVsHierarchyEvents {
            private readonly ProjectNode projectNode;

            public HierarchyEventsSink(ProjectNode projectNode) {
                this.projectNode = projectNode;
            }

            public int OnInvalidateIcon(IntPtr hicon) {
                return VSConstants.S_OK;
            }

            public int OnInvalidateItems(uint itemidParent) {
                return VSConstants.S_OK;
            }

            public int OnItemAdded(uint itemidParent, uint itemidSiblingPrev, uint itemidAdded) {
                return VSConstants.S_OK;
            }

            public int OnItemDeleted(uint itemid) {
                return VSConstants.S_OK;
            }

            public int OnItemsAppended(uint itemidParent) {
                return VSConstants.S_OK;
            }

            public int OnPropertyChanged(uint itemid, int propid, uint flags) {
                return VSConstants.S_OK;
            }
        }

        #endregion

        #region constants
        /// <summary>
        /// The user file extension.
        /// </summary>
        public const string PerUserFileExtension = ".user";

        //This version is different from the one of ANX because we can't load a different framework version in the AppDomain and still communicate with it without using reflection and custom marshalling for every call.
        //The framework version just depends on the version that visual studio uses.
#if DEV11_OR_LATER
        private static readonly FrameworkName DefaultTargetFrameworkMoniker = new FrameworkName(".NETFramework", new Version(4, 5));
#else
        private static readonly FrameworkName DefaultTargetFrameworkMoniker = new FrameworkName(".NETFramework", new Version(4, 0), "Client");
#endif
        #endregion

        #region fields
        /// <summary>
        /// List of output groups names and their associated target
        /// </summary>
        private static KeyValuePair<string, string>[] outputGroupNames =
        {                                      // Name                    Target (MSBuild)
            new KeyValuePair<string, string>("Built",                 "BuiltProjectOutputGroup"),
            new KeyValuePair<string, string>("ContentFiles",          "ContentFilesProjectOutputGroup"),
            new KeyValuePair<string, string>("LocalizedResourceDlls", "SatelliteDllsProjectOutputGroup"),
            new KeyValuePair<string, string>("Documentation",         "DocumentationProjectOutputGroup"),
            new KeyValuePair<string, string>("Symbols",               "DebugSymbolsProjectOutputGroup"),
            new KeyValuePair<string, string>("SourceFiles",           "SourceFilesProjectOutputGroup"),
            new KeyValuePair<string, string>("XmlSerializer",         "SGenFilesOutputGroup"),
        };
        private EventSinkCollection _hierarchyEventSinks = new EventSinkCollection();

        /// <summary>A project will only try to build if it can obtain a lock on this object</summary>
        private volatile static object BuildLock = new object();

        /// <summary>Maps integer ids to project item instances</summary>
        private EventSinkCollection itemIdMap = new EventSinkCollection();

        /// <summary>A service provider call back object provided by the IDE hosting the project manager</summary>
        private ServiceProvider site;

        private TrackDocumentsHelper tracker;

        /// <summary>
        /// This property returns the time of the last change made to this project.
        /// It is not the time of the last change on the project file, but actually of
        /// the in memory project settings.  In other words, it is the last time that 
        /// SetProjectDirty was called.
        /// </summary>
        private DateTime lastModifiedTime;

        /// <summary>
        /// MSBuild engine we are going to use 
        /// </summary>
        private MSBuild.ProjectCollection buildEngine;

        private Microsoft.Build.Framework.ILogger buildLogger;

        private bool useProvidedLogger;

        private MSBuild.Project buildProject;

        private MSBuildExecution.ProjectInstance currentConfig;

        private ConfigProvider configProvider;

        private TaskProvider taskProvider;

        private string filename;

        private Microsoft.VisualStudio.Shell.Url baseUri;

        private string projectHome;

        private bool isDirty;

        private bool projectOpened;

        private bool buildIsPrepared;

        private string errorString;

        private string warningString;

        private ImageHandler imageHandler;

        private Guid projectIdGuid;

        private bool isClosed;

        protected EventTriggering eventTriggeringFlag = EventTriggering.TriggerAll;

        private bool canFileNodesHaveChilds;

        private bool isProjectEventsListener = true;

        /// <summary>
        /// The build dependency list passed to IVsDependencyProvider::EnumDependencies 
        /// </summary>
        private List<IVsBuildDependency> buildDependencyList = new List<IVsBuildDependency>();

        /// <summary>
        /// Defines if Project System supports Project Designer
        /// </summary>
        private bool supportsProjectDesigner;

        private bool showProjectInSolutionPage = true;

        private bool buildInProcess;

        private string sccProjectName;

        private string sccLocalPath;

        private string sccAuxPath;

        private string sccProvider;

        /// <summary>
        /// Flag for controling how many times we register with the Scc manager.
        /// </summary>
        private bool isRegisteredWithScc;

        /// <summary>
        /// Flag for controling query edit should communicate with the scc manager.
        /// </summary>
        protected bool disableQueryEdit;

        /// <summary>
        /// Control if command with potential destructive behavior such as delete should
        /// be enabled for nodes of this project.
        /// </summary>
        private bool canProjectDeleteItems;

        /// <summary>
        /// Member to store output base relative path. Used by OutputBaseRelativePath property
        /// </summary>
        private string outputBaseRelativePath = "bin";

        /// <summary>
        /// Used for flavoring to hold the XML fragments
        /// </summary>
        private XmlDocument xmlFragments;

        /// <summary>
        /// Used to map types to CATID. This provide a generic way for us to do this
        /// and make it simpler for a project to provide it's CATIDs for the different type of objects
        /// for which it wants to support extensibility. This also enables us to have multiple
        /// type mapping to the same CATID if we choose to.
        /// </summary>
        private Dictionary<Type, Guid> catidMapping = new Dictionary<Type, Guid>();

        /// <summary>
        /// The public package implementation.
        /// </summary>
        private ProjectPackage package;

        /// <summary>
        /// Mapping from item names to their hierarchy nodes for all disk-based nodes.
        /// </summary>
        protected readonly Dictionary<string, HierarchyNode> _diskNodes = new Dictionary<string, HierarchyNode>(StringComparer.OrdinalIgnoreCase);

        // Has the object been disposed.
        private bool isDisposed;

        private IVsHierarchy parentHierarchy;
        private int parentHierarchyItemId;

        private List<HierarchyNode> itemsDraggedOrCutOrCopied;
        private CopyPasteDragSource sourceDraggedOrCutOrCopied;
        /// <summary>
        /// Folder node in the process of being created.  First the hierarchy node
        /// is added, then the label is edited, and when that completes/cancels
        /// the folder gets created.
        /// </summary>
        private FolderNode _folderBeingCreated;

        /// <summary>
        /// Changing the framework causes an additional save and loosing of the service provider. We suspend the save because we can't handle that without a service provider.
        /// </summary>
        protected bool isChangingFramework = false;

        #endregion

        #region abstract properties
        /// <summary>
        /// This Guid must match the Guid you registered under
        /// HKLM\Software\Microsoft\VisualStudio\%version%\Projects.
        /// Among other things, the Project framework uses this 
        /// guid to find your project and item templates.
        /// </summary>
        public abstract Guid ProjectGuid
        {
            get;
        }

        /// <summary>
        /// Returns a caption for VSHPROPID_TypeName.
        /// </summary>
        /// <returns></returns>
        public abstract string ProjectType
        {
            get;
        }

        #endregion

        #region virtual properties

        public virtual FrameworkName TargetFrameworkMoniker
        {
            get
            {
                return DefaultTargetFrameworkMoniker;
            }
            set
            {
                
            }
        }

        /// <summary>
        /// Indicates whether or not the project system supports Show All Files.
        /// 
        /// Subclasses will need to return true here, and will need to handle calls 
        /// </summary>
        public virtual bool CanShowAllFiles 
        {
            get 
            {
                return false;
            }
        }

        /// <summary>
        /// Indicates whether or not the project is currently in the mode where its showing all files.
        /// </summary>
        public virtual bool IsShowingAllFiles 
        {
            get 
            {
                return false;
            }
        }

        /// <summary>
        /// Represents the command guid for the project system.  This enables
        /// using CommonConstants.cmdid* commands.
        /// 
        /// By default these commands are disabled if this isn't overridden
        /// with the packages command guid.
        /// </summary>
        public virtual Guid SharedCommandGuid {
            get {
                return CommonConstants.NoSharedCommandsGuid;
            }
        }

        /// <summary>
        /// This is the project instance guid that is peristed in the project file
        /// </summary>
        [System.ComponentModel.BrowsableAttribute(false)]
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")]
        public virtual Guid ProjectIDGuid
        {
            get
            {
                return this.projectIdGuid;
            }
            set
            {
                if (this.projectIdGuid != value)
                {
                    this.projectIdGuid = value;
                    if (this.buildProject != null)
                    {
                        this.SetProjectProperty("ProjectGuid", this.projectIdGuid.ToString("B"));
                    }
                }
            }
        }

        public override bool CanAddFiles
        {
            get
            {
                return true;
            }
        }

        #endregion

        #region properties

        public EventSinkCollection EventSinks
        {
            get { return _hierarchyEventSinks; }
        }

        protected bool IsIdeInCommandLineMode
        {
            get
            {
                bool cmdline = false;
                var shell = this.site.GetService(typeof(SVsShell)) as IVsShell;
                if (shell != null)
                {
                    object obj;
                    Marshal.ThrowExceptionForHR(shell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode, out obj));
                    cmdline = (bool)obj;
                }
                return cmdline;
            }
        }

        /// <summary>
        /// Gets the folder node which is currently being added to the project via
        /// Solution Explorer.
        /// </summary>
        public FolderNode FolderBeingCreated {
            get {
                return _folderBeingCreated;
            }
            set {
                _folderBeingCreated = value;
            }
        }

        enum CopyPasteDragSource {
            None,
            Dragged,
            Cut,
            Copied
        }

        private CopyPasteDragSource SourceDraggedOrCutOrCopied {
            get {
                return this.sourceDraggedOrCutOrCopied;
            }
            set {
                this.sourceDraggedOrCutOrCopied = value;
            }
        }

        public IList<HierarchyNode> ItemsDraggedOrCutOrCopied {
            get {
                return this.itemsDraggedOrCutOrCopied;
            }
        }

        public MSBuildExecution.ProjectInstance CurrentConfig
        {
            get
            {
                return currentConfig;
            }
            set
            {
                currentConfig = value;
            }
        }

        #region overridden properties

        public override string FullPathToChildren {
            get {
                return ProjectHome;
            }
        }

        public override int MenuCommandId
        {
            get
            {
                return VsMenus.IDM_VS_CTXT_PROJNODE;
            }
        }

        public override string Url
        {
            get
            {
                return this.GetMkDocument();
            }
        }

        public override string Caption
        {
            get
            {
                // Default to file name
                string caption = this.buildProject.FullPath;
                if (String.IsNullOrEmpty(caption))
                {
                    if (this.buildProject.GetProperty(ProjectFileConstants.Name) != null)
                    {
                        caption = this.buildProject.GetProperty(ProjectFileConstants.Name).EvaluatedValue;
                        if (caption == null || caption.Length == 0)
                        {
                            caption = this.ItemNode.GetMetadata(ProjectFileConstants.Include);
                        }
                    }
                }
                else
                {
                    caption = Path.GetFileNameWithoutExtension(caption);
                }

                return caption;
            }
        }

        public override Guid ItemTypeGuid
        {
            get
            {
                return this.ProjectGuid;
            }
        }

        public override int ImageIndex
        {
            get
            {
                return (int)ProjectNode.ImageName.Application;
            }
        }


        #endregion

        #region virtual properties

        public virtual string ErrorString
        {
            get
            {
                if (this.errorString == null)
                {
                    this.errorString = SR.GetString(SR.Error, CultureInfo.CurrentUICulture);
                }

                return this.errorString;
            }
        }

        public virtual string WarningString
        {
            get
            {
                if (this.warningString == null)
                {
                    this.warningString = SR.GetString(SR.Warning, CultureInfo.CurrentUICulture);
                }

                return this.warningString;
            }
        }

        /// <summary>
        /// Override this property to specify when the project file is dirty.
        /// </summary>
        protected virtual bool IsProjectFileDirty
        {
            get
            {
                string document = this.GetMkDocument();

                if (String.IsNullOrEmpty(document))
                {
                    return this.isDirty;
                }

                return (this.isDirty || !File.Exists(document));
            }
        }

        /// <summary>
        /// True if the project uses the Project Designer Editor instead of the property page frame to edit project properties.
        /// </summary>
        protected virtual bool SupportsProjectDesigner
        {
            get
            {
                return this.supportsProjectDesigner;
            }
            set
            {
                this.supportsProjectDesigner = value;
            }

        }

        protected virtual Guid ProjectDesignerEditor
        {
            get
            {
                return VSConstants.GUID_ProjectDesignerEditor;
            }
        }

        /// <summary>
        /// Defines the flag that supports the VSHPROPID.ShowProjInSolutionPage
        /// </summary>
        protected virtual bool ShowProjectInSolutionPage
        {
            get
            {
                return this.showProjectInSolutionPage;
            }
            set
            {
                this.showProjectInSolutionPage = value;
            }
        }

        #endregion

        /// <summary>
        /// Gets or sets the ability of a project filenode to have child nodes (sub items).
        /// Example would be C#/VB forms having resx and designer files.
        /// </summary>
        public bool CanFileNodesHaveChilds
        {
            get
            {
                return canFileNodesHaveChilds;
            }
            set
            {
                canFileNodesHaveChilds = value;
            }
        }

        /// <summary>
        /// Gets a service provider object provided by the IDE hosting the project
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
        public IServiceProvider Site
        {
            get
            {
                return this.site;
            }
        }

        /// <summary>
        /// Gets an ImageHandler for the project node.
        /// </summary>
        public ImageHandler ImageHandler
        {
            get
            {
                if (null == imageHandler)
                {
                    imageHandler = new ImageHandler(ProjectIconsImageStripStream);
                }
                return imageHandler;
            }
        }

        protected virtual Stream ProjectIconsImageStripStream 
        {
            get 
            {
                float visualStudioVersion;
                if (!float.TryParse(((EnvDTE.DTE)this.Site.GetService(typeof(EnvDTE.DTE))).Version, NumberStyles.Float, CultureInfo.InvariantCulture, out visualStudioVersion))
                    return typeof(ProjectNode).Assembly.GetManifestResourceStream("Microsoft.VisualStudio.Project.Resources.imagelis_VS2013.png");

                if (visualStudioVersion == 11.0)
                    return typeof(ProjectNode).Assembly.GetManifestResourceStream("Microsoft.VisualStudio.Project.Resources.imagelis_VS2012.png");
                else if (visualStudioVersion >= 12.0)
                    return typeof(ProjectNode).Assembly.GetManifestResourceStream("Microsoft.VisualStudio.Project.Resources.imagelis_VS2013.png");
                else
                    return typeof(ProjectNode).Assembly.GetManifestResourceStream("Microsoft.VisualStudio.Project.Resources.imagelis_VS2010.bmp");
            }
        }

        /// <summary>
        /// Gets the path to the root folder of the project.
        /// </summary>
        public string ProjectHome
        {
            get
            {
                if (projectHome == null)
                {
                    projectHome = CommonUtils.GetAbsoluteDirectoryPath(
                        this.ProjectFolder,
                        this.GetProjectProperty(CommonConstants.ProjectHome, true));
                }

                Debug.Assert(projectHome != null, "ProjectHome should not be null");
                return projectHome;
            }
        }

        /// <summary>
        /// Gets the path to the folder containing the project.
        /// </summary>
        public string ProjectFolder
        {
            get
            {
                return Path.GetDirectoryName(this.filename);
            }
        }

        /// <summary>
        /// Gets or sets the project filename.
        /// </summary>
        public string ProjectFile
        {
            get
            {
                return Path.GetFileName(this.filename);
            }
            set
            {
                this.SetEditLabel(value);
            }
        }

        /// <summary>
        /// Gets the Base Uniform Resource Identifier (URI).
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "URI")]
        public Microsoft.VisualStudio.Shell.Url BaseURI
        {
            get
            {
                if (baseUri == null && this.buildProject != null)
                {
                    string path = CommonUtils.NormalizeDirectoryPath(Path.GetDirectoryName(this.buildProject.FullPath));
                    baseUri = new Url(path);
                }

                Debug.Assert(baseUri != null, "Base URL should not be null. Did you call BaseURI before loading the project?");
                return baseUri;
            }
        }

        protected void BuildProjectLocationChanged()
        {
            baseUri = null;
            projectHome = null;
        }

        /// <summary>
        /// Gets whether or not the project is closed.
        /// </summary>
        public bool IsClosed
        {
            get
            {
                return this.isClosed;
            }
        }

        /// <summary>
        /// Gets whether or not the project is being built.
        /// </summary>
        public bool BuildInProgress {
            get {
                return buildInProcess;
            }
        }


        /// <summary>
        /// Gets or set the relative path to the folder containing the project ouput. 
        /// </summary>
        public virtual string OutputBaseRelativePath
        {
            get
            {
                return this.outputBaseRelativePath;
            }
            set
            {
                if (Path.IsPathRooted(value))
                {
                    // TODO: Maybe bring the exception back instead of automatically fixing this?
                    this.outputBaseRelativePath = CommonUtils.GetRelativeDirectoryPath(ProjectHome, value);
                }

                this.outputBaseRelativePath = value;
            }
        }

        /// <summary>
        /// Gets a collection of integer ids that maps to project item instances
        /// </summary>
        public EventSinkCollection ItemIdMap
        {
            get
            {
                return this.itemIdMap;
            }
        }

        /// <summary>
        /// Get the helper object that track document changes.
        /// </summary>
        public TrackDocumentsHelper Tracker
        {
            get
            {
                return this.tracker;
            }
        }

        /// <summary>
        /// Gets or sets the build logger.
        /// </summary>
        protected Microsoft.Build.Framework.ILogger BuildLogger
        {
            get
            {
                return this.buildLogger;
            }
            set
            {
                this.buildLogger = value;
                this.useProvidedLogger = true;
            }
        }

        /// <summary>
        /// Gets the taskprovider.
        /// </summary>
        protected TaskProvider TaskProvider
        {
            get
            {
                return this.taskProvider;
            }
        }

        /// <summary>
        /// Gets or sets the project file name.
        /// </summary>
        protected string FileName
        {
            get
            {
                return this.filename;
            }
            set 
            {
                this.filename = value;
            }
        }


        /// <summary>
        /// Gets the configuration provider.
        /// </summary>
        public ConfigProvider ConfigProvider
        {
            get
            {
                if (this.configProvider == null)
                {
                    this.configProvider = CreateConfigProvider();
                }

                return this.configProvider;
            }
        }

        /// <summary>
        /// Gets or set whether items can be deleted for this project.
        /// Enabling this feature can have the potential destructive behavior such as deleting files from disk.
        /// </summary>
        public bool CanProjectDeleteItems
        {
            get
            {
                return canProjectDeleteItems;
            }
            set
            {
                canProjectDeleteItems = value;
            }
        }

        /// <summary>
        /// Gets or sets event triggering flags.
        /// </summary>
        public EventTriggering EventTriggeringFlag
        {
            get
            {
                return this.eventTriggeringFlag;
            }
            set
            {
                this.eventTriggeringFlag = value;
            }
        }

        /// <summary>
        /// Defines the build project that has loaded the project file.
        /// </summary>
        public MSBuild.Project BuildProject
        {
            get
            {
                return this.buildProject;
            }
            set
            {
                SetBuildProject(value);
            }
        }

        /// <summary>
        /// Defines the build engine that is used to build the project file.
        /// </summary>
        public MSBuild.ProjectCollection BuildEngine
        {
            get
            {
                return this.buildEngine;
            }
            set
            {
                this.buildEngine = value;
            }
        }

        /// <summary>
        /// The public package implementation.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        public ProjectPackage Package
        {
            get
            {
                return this.package;
            }
            set
            {
                this.package = value;
            }
        }
        #endregion

        #region ctor

        protected ProjectNode(ProjectPackage package)
        {
            this.package = package;

            this.Initialize();
        }

        #endregion

        #region overridden methods

        public override void DeleteFromStorage(string path) {
            if (File.Exists(path)) {
                File.Delete(path);
            }
            base.DeleteFromStorage(path);
        }

        /// <summary>
        /// Sets the properties for the project node.
        /// </summary>
        /// <param name="propid">Identifier of the hierarchy property. For a list of propid values, <see cref="__VSHPROPID"/> </param>
        /// <param name="value">The value to set. </param>
        /// <returns>A success or failure value.</returns>
        public override int SetProperty(int propid, object value)
        {
            __VSHPROPID id = (__VSHPROPID)propid;

            switch (id)
            {
                case __VSHPROPID.VSHPROPID_ParentHierarchy:
                    parentHierarchy = (IVsHierarchy)value;
                    break;

                case __VSHPROPID.VSHPROPID_ParentHierarchyItemid:
                    parentHierarchyItemId = (int)value;
                    break;

                case __VSHPROPID.VSHPROPID_ShowProjInSolutionPage:
                    this.ShowProjectInSolutionPage = (bool)value;
                    return VSConstants.S_OK;
            }

            return base.SetProperty(propid, value);
        }

        /// <summary>
        /// Renames the project node.
        /// </summary>
        /// <param name="label">The new name</param>
        /// <returns>A success or failure value.</returns>
        public override int SetEditLabel(string label)
        {
            // Validate the filename. 
            if (VsUtilities.IsFileNameInvalid(label))
            {
                throw new InvalidOperationException(String.Format(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture), label));
            }
            else if (this.ProjectFolder.Length + label.Length + 1 > NativeMethods.MAX_PATH)
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), label));
            }


            // TODO: Take file extension into account?
            string fileName = Path.GetFileNameWithoutExtension(label);

            // Nothing to do if the name is the same
            string oldFileName = Path.GetFileNameWithoutExtension(this.Url);
            if (String.Equals(oldFileName, label, StringComparison.Ordinal))
            {
                return VSConstants.S_FALSE;
            }

            // Now check whether the original file is still there. It could have been renamed.
            if (!File.Exists(this.Url))
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileOrFolderCannotBeFound, CultureInfo.CurrentUICulture), this.ProjectFile));
            }

            // Get the full file name and then rename the project file.
            string newFile = Path.Combine(this.ProjectFolder, label);
            string extension = Path.GetExtension(this.Url);

            // Make sure it has the correct extension
            if (!String.Equals(Path.GetExtension(newFile), extension, StringComparison.OrdinalIgnoreCase))
            {
                newFile += extension;
            }

            this.RenameProjectFile(newFile);
            return VSConstants.S_OK;
        }

        /// <summary>
        /// Gets the automation object for the project node.
        /// </summary>
        /// <returns>An instance of an EnvDTE.Project implementation object representing the automation object for the project.</returns>
        public override object GetAutomationObject()
        {
            return new Automation.OAProject(this);
        }

        /// <summary>
        /// Gets the properties of the project node. 
        /// </summary>
        /// <param name="propId">The __VSHPROPID of the property.</param>
        /// <returns>A property dependent value. See: <see cref="__VSHPROPID"/> for details.</returns>
        public override object GetProperty(int propId)
        {
            switch ((__VSHPROPID)propId)
            {
                case __VSHPROPID.VSHPROPID_ConfigurationProvider:
                    return this.ConfigProvider;

                case __VSHPROPID.VSHPROPID_ProjectName:
                    return this.Caption;

                case __VSHPROPID.VSHPROPID_ProjectDir:
                    return this.ProjectFolder;

                case __VSHPROPID.VSHPROPID_TypeName:
                    return this.ProjectType;

                case __VSHPROPID.VSHPROPID_ShowProjInSolutionPage:
                    return this.ShowProjectInSolutionPage;

                case __VSHPROPID.VSHPROPID_ExpandByDefault:
                    return true;

                // Use the same icon as if the folder was closed
                case __VSHPROPID.VSHPROPID_OpenFolderIconIndex:
                    return GetProperty((int)__VSHPROPID.VSHPROPID_IconIndex);

                case __VSHPROPID.VSHPROPID_ParentHierarchyItemid:
                    if (parentHierarchy != null) {
                        return (IntPtr)parentHierarchyItemId; // VS requires VT_I4 | VT_INT_PTR
            }
                    break;

                case __VSHPROPID.VSHPROPID_ParentHierarchy:
                    return parentHierarchy;
            }

            switch ((__VSHPROPID2)propId)
            {
                case __VSHPROPID2.VSHPROPID_SupportsProjectDesigner:
                    return this.SupportsProjectDesigner;

                case __VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList:
                    return VsUtilities.CreateSemicolonDelimitedListOfStringFromGuids(this.GetConfigurationIndependentPropertyPages());

                case __VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList:
                    return VsUtilities.CreateSemicolonDelimitedListOfStringFromGuids(this.GetConfigurationDependentPropertyPages());

                case __VSHPROPID2.VSHPROPID_PriorityPropertyPagesCLSIDList:
                    return VsUtilities.CreateSemicolonDelimitedListOfStringFromGuids(this.GetPriorityProjectDesignerPages());

                case __VSHPROPID2.VSHPROPID_Container:
                    return true;
                default:
                    break;
            }

            switch ((__VSHPROPID4)propId)
            {
                case __VSHPROPID4.VSHPROPID_TargetFrameworkMoniker:
                    return this.TargetFrameworkMoniker.FullName;
            }

#if DEV11_OR_LATER && FALSE
            switch ((__VSHPROPID5)propId)
            {
                case __VSHPROPID5.VSHPROPID_ReferenceManagerUser:
                    return Marshal.GetIUnknownForObject(this.GetReferenceManagerUser());

            }
#endif

            return base.GetProperty(propId);
        }

        /// <summary>
        /// Gets the GUID value of the node. 
        /// </summary>
        /// <param name="propid">A __VSHPROPID or __VSHPROPID2 value of the guid property</param>
        /// <param name="guid">The guid to return for the property.</param>
        /// <returns>A success or failure value.</returns>
        public override int GetGuidProperty(int propid, out Guid guid)
        {
            guid = Guid.Empty;
            if ((__VSHPROPID)propid == __VSHPROPID.VSHPROPID_ProjectIDGuid)
            {
                guid = this.ProjectIDGuid;
            }
            else if (propid == (int)__VSHPROPID.VSHPROPID_CmdUIGuid)
            {
                guid = this.ProjectGuid;
            }
            else if ((__VSHPROPID2)propid == __VSHPROPID2.VSHPROPID_ProjectDesignerEditor && this.SupportsProjectDesigner)
            {
                guid = this.ProjectDesignerEditor;
            }
            else
            {
                base.GetGuidProperty(propid, out guid);
            }

            if (guid.CompareTo(Guid.Empty) == 0)
            {
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Sets Guid properties for the project node.
        /// </summary>
        /// <param name="propid">A __VSHPROPID or __VSHPROPID2 value of the guid property</param>
        /// <param name="guid">The guid value to set.</param>
        /// <returns>A success or failure value.</returns>
        public override int SetGuidProperty(int propid, ref Guid guid)
        {
            switch ((__VSHPROPID)propid)
            {
                case __VSHPROPID.VSHPROPID_ProjectIDGuid:
                    this.ProjectIDGuid = guid;
                    return VSConstants.S_OK;
            }
            return VSConstants.DISP_E_MEMBERNOTFOUND;
        }

        /// <summary>
        /// Removes items from the hierarchy. 
        /// </summary>
        /// <devdoc>Project overwrites this.</devdoc>
        public override void Remove(bool removeFromStorage)
        {
            // the project will not be deleted from disk, just removed      
            if (removeFromStorage)
            {
                return;
            }

            // Remove the entire project from the solution
            IVsSolution solution = this.Site.GetService(typeof(SVsSolution)) as IVsSolution;
            uint iOption = 1; // SLNSAVEOPT_PromptSave
            ErrorHandler.ThrowOnFailure(solution.CloseSolutionElement(iOption, this.GetOuterInterface<IVsHierarchy>(), 0));
        }

        /// <summary>
        /// Gets the moniker for the project node. That is the full path of the project file.
        /// </summary>
        /// <returns>The moniker for the project file.</returns>
        public override string GetMkDocument()
        {
            Debug.Assert(!String.IsNullOrEmpty(this.filename));
            Debug.Assert(this.BaseURI != null && !String.IsNullOrEmpty(this.BaseURI.AbsoluteUrl));
            return CommonUtils.GetAbsoluteFilePath(this.BaseURI.AbsoluteUrl, this.filename);
        }

        /// <summary>
        /// Disposes the project node object.
        /// </summary>
        /// <param name="disposing">Flag determining ehether it was deterministic or non deterministic clean up.</param>
        protected override void Dispose(bool disposing)
        {
            if (this.isDisposed)
            {
                return;
            }

            try
            {
                try
                {
                    this.UnRegisterProject();
                }
                finally
                {
                    try
                    {
                        this.RegisterClipboardNotifications(false);
                    }
                    finally
                    {
                        try
                        {
                            if (this.site != null)
                            {
                                this.site.Dispose();
                            }
                        }
                        finally
                        {
                            this.buildEngine = null;
                        }
                    }
                }

                if (this.buildProject != null)
                {
                    this.buildProject.ProjectCollection.UnloadProject(this.buildProject);
                    this.buildProject.ProjectCollection.UnloadProject(this.buildProject.Xml);
                    this.SetBuildProject(null);
                }

                if (null != imageHandler)
                {
                    imageHandler.Close();
                    imageHandler = null;
                }
            }
            finally
            {
                base.Dispose(disposing);
                this.isDisposed = true;
            }
        }

        /// <summary>
        /// Handles command status on the project node. If a command cannot be handled then the base should be called.
        /// </summary>
        /// <param name="cmdGroup">A unique identifier of the command group. The pguidCmdGroup parameter can be NULL to specify the standard group.</param>
        /// <param name="cmd">The command to query status for.</param>
        /// <param name="pCmdText">Pointer to an OLECMDTEXT structure in which to return the name and/or status information of a single command. Can be NULL to indicate that the caller does not require this information.</param>
        /// <param name="result">An out parameter specifying the QueryStatusResult of the command.</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
        public override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result)
        {
            if (cmdGroup == VsMenus.guidStandardCommandSet97)
            {
                switch ((VsCommands)cmd)
                {
                    case VsCommands.Copy:
                    case VsCommands.Paste:
                    case VsCommands.Cut:
                    case VsCommands.Rename:
                    case VsCommands.Exit:
                    case VsCommands.ProjectSettings:
                    case VsCommands.UnloadProject:
                        result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
                        return VSConstants.S_OK;

                    case VsCommands.CancelBuild:
                        result |= QueryStatusResult.SUPPORTED;
                        if (this.buildInProcess)
                            result |= QueryStatusResult.ENABLED;
                        else
                        result |= QueryStatusResult.INVISIBLE;
                        return VSConstants.S_OK;

                    case VsCommands.NewFolder:
                        result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
                        return VSConstants.S_OK;

                    case VsCommands.SetStartupProject:
                        result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
                        return VSConstants.S_OK;
                }
            }
            else if (cmdGroup == VsMenus.guidStandardCommandSet2K)
            {

                switch ((VsCommands2K)cmd)
                {
                    case VsCommands2K.ADDREFERENCE:
                        if (GetReferenceContainer() != null) {
                        result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
                        } else {
                            result |= QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE;
                        }
                        return VSConstants.S_OK;

                    case VsCommands2K.EXCLUDEFROMPROJECT:
                        result |= QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE;
                        return VSConstants.S_OK;

                }
            }
            else
            {
                return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP;
            }

            return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result);
        }

        /// <summary>
        /// Handles command execution.
        /// </summary>
        /// <param name="cmdGroup">Unique identifier of the command group</param>
        /// <param name="cmd">The command to be executed.</param>
        /// <param name="nCmdexecopt">Values describe how the object should execute the command.</param>
        /// <param name="pvaIn">Pointer to a VARIANTARG structure containing input arguments. Can be NULL</param>
        /// <param name="pvaOut">VARIANTARG structure to receive command output. Can be NULL.</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
        public override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
        {
            if (cmdGroup == VsMenus.guidStandardCommandSet97)
            {
                switch ((VsCommands)cmd)
                {

                    case VsCommands.UnloadProject:
                        return this.UnloadProject();
                    case VsCommands.CleanSel:
                    case VsCommands.CleanCtx:
                        return this.CleanProject();
                }
            }

            return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut);
        }

        protected override int StartDebug()
        {
            return VSConstants.E_NOTIMPL;
        }

        /// <summary>
        /// Get the boolean value for the deletion of a project item
        /// </summary>
        /// <param name="deleteOperation">A flag that specifies the type of delete operation (delete from storage or remove from project)</param>
        /// <returns>true if item can be deleted from project</returns>
        public override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation)
        {
            if (deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Returns a specific Document manager to handle opening and closing of the Project(Application) Designer if projectdesigner is supported.
        /// </summary>
        /// <returns>Document manager object</returns>
        public override DocumentManager GetDocumentManager()
        {
            if (this.SupportsProjectDesigner)
            {
                return new ProjectDesignerDocumentManager(this);
            }
            return null;
        }

        #endregion

        #region virtual methods

        public virtual string GetExistingFilesFilter()
        {
            return SR.GetString(SR.AllFilesFilter);
        }

        /// <summary>
        /// Creates a reference node for the given file returning the node, or returns null
        /// if the file doesn't represent a valid file which can be referenced.
        /// </summary>
        public virtual ReferenceNode CreateReferenceNodeForFile(string filename)
        {
#if FALSE
            return new ComReferenceNode(this.ProjectMgr, selectorData);
#endif
            return null;
        }

        /// <summary>
        /// Executes a wizard.
        /// </summary>
        /// <param name="parentNode">The node to which the wizard should add item(s).</param>
        /// <param name="itemName">The name of the file that the user typed in.</param>
        /// <param name="wizardToRun">The name of the wizard to run.</param>
        /// <param name="dlgOwner">The owner of the dialog box.</param>
        /// <returns>A VSADDRESULT enum value describing success or failure.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dlg")]
        public virtual VSADDRESULT RunWizard(HierarchyNode parentNode, string itemName, string wizardToRun, IntPtr dlgOwner)
        {
            Debug.Assert(!String.IsNullOrEmpty(itemName), "The Add item dialog was passing in a null or empty item to be added to the hierrachy.");
            Debug.Assert(!String.IsNullOrEmpty(this.ProjectHome), "ProjectHome is not specified for this project.");

            VsUtilities.ArgumentNotNull("parentNode", parentNode);
            VsUtilities.ArgumentNotNullOrEmpty("itemName", itemName);

            // We just validate for length, since we assume other validation has been performed by the dlgOwner.
            if (CommonUtils.GetAbsoluteFilePath(this.ProjectHome, itemName).Length >= NativeMethods.MAX_PATH)
            {
                string errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), itemName);
                if (!VsUtilities.IsInAutomationFunction(this.Site))
                {
                    string title = null;
                    OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
                    OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
                    OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
                    VsShellUtilities.ShowMessageBox(this.Site, title, errorMessage, icon, buttons, defaultButton);
                    return VSADDRESULT.ADDRESULT_Failure;
                }
                else
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }


            // Build up the ContextParams safearray
            //  [0] = Wizard type guid  (bstr)
            //  [1] = Project name  (bstr)
            //  [2] = ProjectItems collection (bstr)
            //  [3] = Local Directory (bstr)
            //  [4] = Filename the user typed (bstr)
            //  [5] = Product install Directory (bstr)
            //  [6] = Run silent (bool)

            object[] contextParams = new object[7];
            contextParams[0] = EnvDTE.Constants.vsWizardAddItem;
            contextParams[1] = this.Caption;
            object automationObject = parentNode.GetAutomationObject();
            if (automationObject is EnvDTE.Project)
            {
                EnvDTE.Project project = (EnvDTE.Project)automationObject;
                contextParams[2] = project.ProjectItems;
            }
            else
            {
                // This would normally be a folder unless it is an item with subitems
                EnvDTE.ProjectItem item = (EnvDTE.ProjectItem)automationObject;
                contextParams[2] = item.ProjectItems;
            }

            contextParams[3] = this.ProjectHome;

            contextParams[4] = itemName;

            object objInstallationDir = null;
            IVsShell shell = (IVsShell)this.GetService(typeof(IVsShell));
            ErrorHandler.ThrowOnFailure(shell.GetProperty((int)__VSSPROPID.VSSPROPID_InstallDirectory, out objInstallationDir));
            string installDir = CommonUtils.NormalizeDirectoryPath((string)objInstallationDir);

            contextParams[5] = installDir;

            contextParams[6] = true;

            IVsExtensibility3 ivsExtensibility = this.GetService(typeof(IVsExtensibility)) as IVsExtensibility3;
            Debug.Assert(ivsExtensibility != null, "Failed to get IVsExtensibility3 service");
            if (ivsExtensibility == null)
            {
                return VSADDRESULT.ADDRESULT_Failure;
            }

            // Determine if we have the trust to run this wizard.
            IVsDetermineWizardTrust wizardTrust = this.GetService(typeof(SVsDetermineWizardTrust)) as IVsDetermineWizardTrust;
            if (wizardTrust != null)
            {
                Guid guidProjectAdding = Guid.Empty;
                ErrorHandler.ThrowOnFailure(wizardTrust.OnWizardInitiated(wizardToRun, ref guidProjectAdding));
            }

            int wizResultAsInt;
            try
            {
                Array contextParamsAsArray = contextParams;

                int result = ivsExtensibility.RunWizardFile(wizardToRun, (int)dlgOwner, ref contextParamsAsArray, out wizResultAsInt);

                if (!ErrorHandler.Succeeded(result) && result != VSConstants.OLE_E_PROMPTSAVECANCELLED)
                {
                    ErrorHandler.ThrowOnFailure(result);
                }
            }
            finally
            {
                if (wizardTrust != null)
                {
                    ErrorHandler.ThrowOnFailure(wizardTrust.OnWizardCompleted());
                }
            }

            EnvDTE.wizardResult wizardResult = (EnvDTE.wizardResult)wizResultAsInt;

            switch (wizardResult)
            {
                default:
                    return VSADDRESULT.ADDRESULT_Cancel;
                case wizardResult.wizardResultSuccess:
                    return VSADDRESULT.ADDRESULT_Success;
                case wizardResult.wizardResultFailure:
                    return VSADDRESULT.ADDRESULT_Failure;
            }
        }

        /// <summary>
        /// This overrides the base class method to show the VS 2005 style Add reference dialog. The ProjectNode implementation
        /// shows the VS 2003 style Add Reference dialog.
        /// </summary>
        /// <returns>S_OK if succeeded. Failure other wise</returns>
        public virtual int AddProjectReference()
        {
#if DEV11_OR_LATER
            IVsReferenceManager manager = GetService(typeof(SVsReferenceManager)) as IVsReferenceManager;

            try
            {
                // call the container to open the add reference dialog.
                if (manager != null)
                {
                    // Let the project know not to show itself in the Add Project Reference Dialog page
                    ShowProjectInSolutionPage = false;

                    IReferenceContainer referenceContainer = this.GetReferenceContainer();
                    
                    //var context = manager.CreateProviderContext(((ReferenceContainerNode)GetReferenceContainer()).ProviderGuid);
                    Type type = manager.GetType();

                    //var context = manager.CreateProviderContext(((ReferenceContainerNode)this.GetReferenceContainer()).ProviderGuid);
                    IVsAssemblyReferenceProviderContext assemblyProvider = (IVsAssemblyReferenceProviderContext)manager.CreateProviderContext(new Guid("9A341D95-5A64-11D3-BFF9-00C04F990235"));
                    assemblyProvider.TargetFrameworkMoniker = this.TargetFrameworkMoniker.FullName;
                    assemblyProvider.Tabs = (uint)__VSASSEMBLYPROVIDERTAB.TAB_ASSEMBLY_ALL;
                    assemblyProvider.SupportsRetargeting = true;

                    var frameworkRegKey = ToolLocationHelper.GetDotNetFrameworkRootRegistryKey(TargetDotNetFrameworkVersion.Version45);
                    //They don't want the hive name.
                    if (frameworkRegKey.StartsWith("HKEY_LOCAL_MACHINE\\"))
                        frameworkRegKey = frameworkRegKey.Substring("HKEY_LOCAL_MACHINE\\".Length);

                    var searchDirectories = ToolLocationHelper.GetAssemblyFoldersExInfo(frameworkRegKey, ToolLocationHelper.GetDotNetFrameworkVersionFolderPrefix(TargetDotNetFrameworkVersion.Version45), "AssemblyFoldersEx", null, null, System.Reflection.ProcessorArchitecture.MSIL);

                    assemblyProvider.AssemblySearchPaths = string.Join(";", searchDirectories.Select((x) => x.DirectoryPath).Where((x) => !string.IsNullOrEmpty(x)));

                    IVsProjectReferenceProviderContext projectProvider = (IVsProjectReferenceProviderContext)manager.CreateProviderContext(new Guid("51ECA6BD-5AE4-43F0-AA76-DD0A7B08F40C"));
                    projectProvider.CurrentProject = this;

                    IVsFileReferenceProviderContext fileReferenceProvider = (IVsFileReferenceProviderContext)manager.CreateProviderContext(new Guid("7B069159-FF02-4752-93E8-96B3CADF441A"));
                    fileReferenceProvider.DefaultBrowseLocation = Path.GetDirectoryName(ProjectHome);
                    fileReferenceProvider.BrowseFilter = "Component Files (*.dll;*.exe)|*.dll;*.exe|All Files (*.*)|*.*";

                    /*IVsPlatformReferenceProviderContext platformReferenceProvider = (IVsPlatformReferenceProviderContext)manager.CreateProviderContext(new Guid("97324595-E3F9-4AA8-85B7-DC941E812152"));
                    platformReferenceProvider.TargetFrameworkMoniker = this.TargetFrameworkMoniker.FullName;
                    //platformReferenceProvider.ExpandSDKContents = true;
                    platformReferenceProvider.VisualStudioVersion = "11.0";*/
                    
                    /*var frameworkService2 = this.GetService(typeof(SVsFrameworkMultiTargeting)) as IVsFrameworkMultiTargeting2;
                    if (frameworkService2 != null)
                    {
                        List<string> sdkPaths = new List<string>();
                        Array sdks = frameworkService2.GetSDKRootFolders();
                        foreach (string path in sdks)
                        {
                            sdkPaths.AddRange(frameworkService2.GetSDKReferences(path).Cast<string>());
                        }
                        platformReferenceProvider.SDKDirectoryRoot = sdkPaths.FirstOrDefault();
                        platformReferenceProvider.AssemblySearchPaths = string.Join(";", sdkPaths);
                    }*/

                    foreach (ReferenceNode reference in referenceContainer.EnumReferences())
                    {
                        if (reference is ProjectReferenceNode)
                        {
                            ProjectReferenceNode projectReference = (ProjectReferenceNode)reference;

                            //The IVsProjectReferenceProviderContext can't handle normal IVsProjectReferences, he needs his references as projectIdentities.
                            var identitiy = new ProjectIdentity(projectReference.ReferencedProjectGuid.ToString("B"), projectReference.Name, projectReference.FullPath, projectReference.UniqueName, this.ProjectMgr);
                            identitiy.AlreadyReferenced = true;

                            projectProvider.AddReference(identitiy);
                        }
                        else if (reference is AssemblyReferenceNode)
                        {
                            AssemblyReferenceNode assemblyReference = (AssemblyReferenceNode)reference;

                            var fileIdentity = new FileIdentity(assemblyReference.FullPath);
                            fileIdentity.AlreadyReferenced = true;

                            fileReferenceProvider.AddReference(fileIdentity);

                            //TODO: Feststellen ob die Assembly als Framework-Assembly vorhanden ist oder nicht.
                            var assemblyIdentity = new AssemblyIdentity(assemblyReference.AssemblyName, assemblyReference.FullPath, assemblyReference.IsFrameworkAssembly, this.TargetFrameworkMoniker.FullName);

                            assemblyProvider.AddReference(assemblyIdentity);
                        }
                    }

                    var contexts = new IVsReferenceProviderContext[] {
                        assemblyProvider,
                        projectProvider,
                        fileReferenceProvider,
                        //platformReferenceProvider,
                    };

                    var userReferenceManager = new ReferenceManagerUser(contexts, this.GetReferenceContainer());

                    string location = this.GetType().Assembly.Location;

                    // call the container to open the add reference dialog.
                    manager.ShowReferenceManager(
                        userReferenceManager, 
                        SR.GetString(SR.AddReferenceDialogTitle), 
                        "VS.AddReference",
                        assemblyProvider.ProviderGuid,
                        true);
                }
            }
            catch (COMException e)
            {
                Trace.WriteLine("Exception : " + e.Message);
                return e.ErrorCode;
            }
            finally
            {
                // Let the project know it can show itself in the Add Project Reference Dialog page
                ShowProjectInSolutionPage = true;
            }
#else

            IVsComponentSelectorDlg4 componentDialog;
            Guid guidEmpty = Guid.Empty;
            VSCOMPONENTSELECTORTABINIT[] tabInit = new VSCOMPONENTSELECTORTABINIT[4];
            string strBrowseLocations = Path.GetDirectoryName(ProjectHome);

            //Add the Project page
            tabInit[0].dwSize = (uint)Marshal.SizeOf(typeof(VSCOMPONENTSELECTORTABINIT));
            // Tell the Add Reference dialog to call hierarchies GetProperty with the following
            // propID to enable filtering out ourself from the Project to Project reference
            tabInit[0].varTabInitInfo = (int)__VSHPROPID.VSHPROPID_ShowProjInSolutionPage;
            tabInit[0].guidTab = VSConstants.GUID_SolutionPage;

            // Add the Browse for file page            
            tabInit[1].dwSize = (uint)Marshal.SizeOf(typeof(VSCOMPONENTSELECTORTABINIT));
            tabInit[1].guidTab = VSConstants.GUID_COMPlusPage;
            tabInit[1].varTabInitInfo = 0;

            // Add the Browse for file page            
            tabInit[2].dwSize = (uint)Marshal.SizeOf(typeof(VSCOMPONENTSELECTORTABINIT));
            tabInit[2].guidTab = VSConstants.GUID_BrowseFilePage;
            tabInit[2].varTabInitInfo = 0;

            // Add the WebPI page
            tabInit[3].dwSize = (uint)Marshal.SizeOf(typeof(VSCOMPONENTSELECTORTABINIT));
            tabInit[3].guidTab = typeof(WebPiComponentPickerControl).GUID;
            tabInit[3].varTabInitInfo = 0;

            uint pX = 0, pY = 0;
            
            componentDialog = GetService(typeof(SVsComponentSelectorDlg)) as IVsComponentSelectorDlg4;
            try
            {
                // call the container to open the add reference dialog.
                if (componentDialog != null)
                {
                    // Let the project know not to show itself in the Add Project Reference Dialog page
                    ShowProjectInSolutionPage = false;

                    // call the container to open the add reference dialog.
                    ErrorHandler.ThrowOnFailure(componentDialog.ComponentSelectorDlg5(
                        (System.UInt32)(__VSCOMPSELFLAGS.VSCOMSEL_MultiSelectMode | __VSCOMPSELFLAGS.VSCOMSEL_IgnoreMachineName),
                        (IVsComponentUser)this,
                        0,
                        null,
                DynamicProjectSR.GetString(Microsoft.VisualStudio.Project.SR.AddReferenceDialogTitle),   // Title
                        "VS.AddReference",						  // Help topic
                        ref pX,
                        ref pY,
                        (uint)tabInit.Length,
                        tabInit,
                        ref guidEmpty,
                        AddReferenceExtensions,
                        ref strBrowseLocations,
                        this.TargetFrameworkMoniker.FullName));
                }
            }
            catch (COMException e)
            {
                Trace.WriteLine("Exception : " + e.Message);
                return e.ErrorCode;
            }
            finally
            {
                // Let the project know it can show itself in the Add Project Reference Dialog page
                ShowProjectInSolutionPage = true;
            }
#endif
            
            return VSConstants.S_OK;
        }

        protected virtual string AddReferenceExtensions
        {
            get
            {
                return "Dynamic Link Libraries (*.dll)\0*.dll\0All Files (*.*)\0*.*\0";
            }
        }

        /// <summary>
        /// Returns the Compiler associated to the project 
        /// </summary>
        /// <returns>Null</returns>
        public virtual ICodeCompiler GetCompiler()
        {

            return null;
        }

        /// <summary>
        /// Override this method if you have your own project specific
        /// subclass of ProjectOptions
        /// </summary>
        /// <returns>This method returns a new instance of the ProjectOptions base class.</returns>
        public virtual CompilerParameters CreateProjectOptions()
        {
            return new CompilerParameters();
        }

        /// <summary>
        /// Loads a project file. Called from the factory CreateProject to load the project.
        /// </summary>
        /// <param name="fileName">File name of the project that will be created. </param>
        /// <param name="location">Location where the project will be created.</param>
        /// <param name="name">If applicable, the name of the template to use when cloning a new project.</param>
        /// <param name="flags">Set of flag values taken from the VSCREATEPROJFLAGS enumeration.</param>
        /// <param name="iidProject">Identifier of the interface that the caller wants returned. </param>
        /// <param name="canceled">An out parameter specifying if the project creation was canceled</param>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "iid")]
        public virtual void Load(string fileName, string location, string name, uint flags, ref Guid iidProject, out int canceled)
        {
            using (new DebugTimer("ProjectLoad"))
            {
                try
                {
                    this.disableQueryEdit = true;

                    // set up public members and icons
                    canceled = 0;

                    this.ProjectMgr = this;

                    if ((flags & (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) == (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE)
                    {
                        // we need to generate a new guid for the project
                        this.projectIdGuid = Guid.NewGuid();
                    }
                    else
                    {
                        this.SetProjectGuidFromProjectFile();
                    }

                    // This is almost a No op if the engine has already been instantiated in the factory.
                    this.buildEngine = VsUtilities.InitializeMsBuildEngine(this.buildEngine, this.Site);

                    // based on the passed in flags, this either reloads/loads a project, or tries to create a new one
                    // now we create a new project... we do that by loading the template and then saving under a new name
                    // we also need to copy all the associated files with it.					
                    if ((flags & (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) == (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE)
                    {
                        Debug.Assert(!String.IsNullOrEmpty(fileName) && File.Exists(fileName), "Invalid filename passed to load the project. A valid filename is expected");

                        // This should be a very fast operation if the build project is already initialized by the Factory.
                        SetBuildProject(VsUtilities.ReinitializeMsBuildProject(this.buildEngine, fileName, this.buildProject));

                        // Compute the file name
                        // We try to solve two problems here. When input comes from a wizzard in case of zipped based projects 
                        // the parameters are different.
                        // In that case the filename has the new filename in a temporay path.

                        // First get the extension from the template.
                        // Then get the filename from the name.
                        // Then create the new full path of the project.
                        string extension = Path.GetExtension(fileName);

                        string tempName = String.Empty;

                        // We have to be sure that we are not going to lose data here. If the project name is a.b.c then for a project that was based on a zipped template(the wizard calls us) GetFileNameWithoutExtension will suppress "c".
                        // We are going to check if the parameter "name" is extension based and the extension is the same as the one from the "filename" parameter.
                        string tempExtension = Path.GetExtension(name);
                        if (!String.IsNullOrEmpty(tempExtension))
                        {
                            bool isSameExtension = (String.Equals(tempExtension, extension, StringComparison.OrdinalIgnoreCase));

                            if (isSameExtension)
                            {
                                tempName = Path.GetFileNameWithoutExtension(name);
                            }
                            // If the tempExtension is not the same as the extension that the project name comes from then assume that the project name is a dotted name.
                            else
                            {
                                tempName = Path.GetFileName(name);
                            }
                        }
                        else
                        {
                            tempName = Path.GetFileName(name);
                        }

                        Debug.Assert(!String.IsNullOrEmpty(tempName), "Could not compute project name");
                        string tempProjectFileName = tempName + extension;
                        this.filename = CommonUtils.GetAbsoluteFilePath(location, tempProjectFileName);

                        // Initialize the common project properties.
                        this.InitializeProjectProperties();

                        ErrorHandler.ThrowOnFailure(this.Save(this.filename, 1, 0));

                        string unresolvedProjectHome = this.GetProjectProperty(CommonConstants.ProjectHome);
                        string basePath = CommonUtils.GetAbsoluteDirectoryPath(Path.GetDirectoryName(fileName), unresolvedProjectHome);
                        string baseLocation = CommonUtils.GetAbsoluteDirectoryPath(location, unresolvedProjectHome);

                        if (!CommonUtils.IsSameDirectory(basePath, baseLocation))
                        {
                            // now we do have the project file saved. we need to create embedded files.
                            foreach (MSBuild.ProjectItem item in this.BuildProject.Items)
                            {
                                // Ignore the item if it is a reference or folder
                                if (this.FilterItemTypeToBeAddedToHierarchy(item.ItemType))
                                {
                                    continue;
                                }

                                // MSBuilds tasks/targets can create items (such as object files),
                                // such items are not part of the project per say, and should not be displayed.
                                // so ignore those items.
                                if (!this.IsItemTypeFileType(item.ItemType))
                                {
                                    continue;
                                }

                                string strRelFilePath = item.EvaluatedInclude;
                                string strPathToFile;
                                string newFileName;
                                // taking the base name from the project template + the relative pathname,
                                // and you get the filename
                                strPathToFile = CommonUtils.GetAbsoluteFilePath(basePath, strRelFilePath);
                                // the new path should be the base dir of the new project (location) + the rel path of the file
                                newFileName = CommonUtils.GetAbsoluteFilePath(baseLocation, strRelFilePath);
                                // now the copy file
                                AddFileFromTemplate(strPathToFile, newFileName);
                            }
                        }
                    }
                    else
                    {
                        this.filename = fileName;
                    }

                    // now reload to fix up references
                    this.Reload();
                }
                finally
                {
                    this.disableQueryEdit = false;
                }
            }
        }

        /// <summary>
        /// Called to add a file to the project from a template.
        /// Override to do it yourself if you want to customize the file
        /// </summary>
        /// <param name="source">Full path of template file</param>
        /// <param name="target">Full path of file once added to the project</param>
        public virtual void AddFileFromTemplate(string source, string target)
        {
            VsUtilities.ArgumentNotNullOrEmpty("source", source);
            VsUtilities.ArgumentNotNullOrEmpty("target", target);

            try
            {
                string directory = Path.GetDirectoryName(target);
                if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }

                FileInfo fiOrg = new FileInfo(source);
                FileInfo fiNew = fiOrg.CopyTo(target, true);

                fiNew.Attributes = FileAttributes.Normal; // remove any read only attributes.
            }
            catch (IOException e)
            {
                Trace.WriteLine("Exception : " + e.Message);
            }
            catch (UnauthorizedAccessException e)
            {
                Trace.WriteLine("Exception : " + e.Message);
            }
            catch (ArgumentException e)
            {
                Trace.WriteLine("Exception : " + e.Message);
            }
            catch (NotSupportedException e)
            {
                Trace.WriteLine("Exception : " + e.Message);
            }
        }

        /// <summary>
        /// Called when the project opens an editor window for the given file
        /// </summary>
        public virtual void OnOpenItem(string fullPathToSourceFile)
        {
        }

        /// <summary>
        /// This add methos adds the "key" item to the hierarchy, potentially adding other subitems in the process
        /// This method may recurse if the parent is an other subitem
        /// 
        /// </summary>
        /// <param name="subitems">List of subitems not yet added to the hierarchy</param>
        /// <param name="key">Key to retrieve the target item from the subitems list</param>
        /// <returns>Newly added node</returns>
        /// <remarks>If the parent node was found we add the dependent item to it otherwise we add the item ignoring the "DependentUpon" metatdata</remarks>
        protected virtual HierarchyNode AddDependentFileNode(IDictionary<String, MSBuild.ProjectItem> subitems, string key)
        {
            VsUtilities.ArgumentNotNull("subitems", subitems);

            MSBuild.ProjectItem item = subitems[key];
            subitems.Remove(key);

            HierarchyNode newNode;
            HierarchyNode parent = null;

            string dependentOf = item.GetMetadataValue(ProjectFileConstants.DependentUpon);
            Debug.Assert(String.Compare(dependentOf, key, StringComparison.OrdinalIgnoreCase) != 0, "File dependent upon itself is not valid. Ignoring the DependentUpon metadata");
            if (subitems.ContainsKey(dependentOf))
            {
                // The parent item is an other subitem, so recurse into this method to add the parent first
                parent = AddDependentFileNode(subitems, dependentOf);
            }
            else
            {
                // See if the parent node already exist in the hierarchy
                uint parentItemID;
                string path = CommonUtils.GetAbsoluteFilePath(this.ProjectHome, dependentOf);
                if (ErrorHandler.Succeeded(this.ParseCanonicalName(path, out parentItemID)) &&
                    parentItemID != 0)
                    parent = this.NodeFromItemId(parentItemID);
                Debug.Assert(parent != null, "File dependent upon a non existing item or circular dependency. Ignoring the DependentUpon metadata");
            }

            // If the parent node was found we add the dependent item to it otherwise we add the item ignoring the "DependentUpon" metatdata
            if (parent != null)
                newNode = this.AddDependentFileNodeToNode(item, parent);
            else
                newNode = this.AddIndependentFileNode(item, GetItemParentNode(item));

            return newNode;
        }

        /// <summary>
        /// Do the build by invoking msbuild
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "vsopts")]
        public virtual void BuildAsync(uint vsopts, string config, string platform, IVsOutputWindowPane output, string target, IEnumerable<string> files, Action<MSBuildResult, string> uiThreadCallback)
        {
        }

        public virtual int StopBuild(uint vsopts, bool sync)
        {
            if (sync)
            {
                EndBuild(vsopts, null, true, UIThread.Instance.IsUIThread);
            }
            else
            {
                System.Threading.Tasks.Task.Run(() => { EndBuild(vsopts, null, true, UIThread.Instance.IsUIThread); });
            }

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Return the value of a project property
        /// </summary>
        /// <param name="propertyName">Name of the property to get</param>
        /// <param name="resetCache">True to avoid using the cache</param>
        /// <returns>null if property does not exist, otherwise value of the property</returns>
        public virtual string GetProjectProperty(string propertyName, bool resetCache)
        {
            MSBuildExecution.ProjectPropertyInstance property = GetMsBuildProperty(propertyName, resetCache);
            if (property == null)
                return null;

            return property.EvaluatedValue;
        }

        /// <summary>
        /// Return the value of a project property in it's unevalauted form.
        /// 
        /// New in 1.5.
        /// </summary>
        /// <param name="propertyName">Name of the property to get</param>
        public virtual string GetUnevaluatedProperty(string propertyName)
        {
            var res = this.buildProject.GetProperty(propertyName);

            if (res != null)
            {
                return res.UnevaluatedValue;
            }
            return null;
        }

        /// <summary>
        /// Set value of project property
        /// </summary>
        /// <param name="propertyName">Name of property</param>
        /// <param name="propertyValue">Value of property</param>
        public virtual void SetProjectProperty(string propertyName, string propertyValue)
        {
            VsUtilities.ArgumentNotNull("propertyName", propertyName);

            string oldValue = null;
            ProjectPropertyInstance oldProp = GetMsBuildProperty(propertyName, true);
            if (oldProp != null)
                oldValue = oldProp.EvaluatedValue;
            if (propertyValue == null)
            {
                // if property already null, do nothing
                if (oldValue == null)
                    return;
                // otherwise, set it to empty
                propertyValue = String.Empty;
            }

            // Only do the work if this is different to what we had before
            if (String.Compare(oldValue, propertyValue, StringComparison.Ordinal) != 0)
            {
                // Check out the project file.
                if (!this.QueryEditProjectFile(false))
                {
                    throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
                }

                var newProp = this.buildProject.SetProperty(propertyName, propertyValue);
                RaiseProjectPropertyChanged(propertyName, oldValue, propertyValue);

                // property cache will need to be updated
                this.currentConfig = null;
                this.SetProjectFileDirty(true);
            }
            return;
        }



        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
        public virtual CompilerParameters GetProjectOptions(string config, string platform)
        {
            // This needs to be commented out because if you build for Debug the properties from the Debug 
            // config are cached. When you change configurations the old props are still cached, and 
            // building for release the properties from the Debug config are used. This may not be the best 
            // fix as every time you get properties the objects are reloaded, so for perf it is bad, but 
            // for making it work it is necessary (reload props when a config is changed?).
            ////if(this.options != null)
            ////    return this.options;

            CompilerParameters options = CreateProjectOptions();

            if (config == null)
                return options;

            options.GenerateExecutable = true;

            this.SetConfiguration(config, platform);

            string outputPath = this.GetOutputPath(this.currentConfig);
            if (!String.IsNullOrEmpty(outputPath))
            {
                // absolutize relative to project folder location
                outputPath = CommonUtils.GetAbsoluteDirectoryPath(this.ProjectHome, outputPath);
            }

            // Set some default values
            options.OutputAssembly = outputPath + this.Caption + ".exe";

            options.OutputAssembly = outputPath + this.GetAssemblyName(config, platform);

            string outputtype = GetProjectProperty(ProjectFileConstants.OutputType, false);
            if (!string.IsNullOrEmpty(outputtype))
            {
                outputtype = outputtype.ToLower(CultureInfo.InvariantCulture);
            }


            options.MainClass = GetProjectProperty("StartupObject", false);

            //    other settings from CSharp we may want to adopt at some point...
            //    AssemblyKeyContainerName = ""  //This is the key file used to sign the interop assembly generated when importing a com object via add reference
            //    AssemblyOriginatorKeyFile = ""
            //    DelaySign = "false"
            //    DefaultClientScript = "JScript"
            //    DefaultHTMLPageLayout = "Grid"
            //    DefaultTargetSchema = "IE50"
            //    PreBuildEvent = ""
            //    PostBuildEvent = ""
            //    RunPostBuildEvent = "OnBuildSuccess"

            if (GetBoolAttr(this.currentConfig, "DebugSymbols"))
            {
                options.IncludeDebugInformation = true;
            }

            if (GetBoolAttr(this.currentConfig, "RegisterForComInterop"))
            {
            }

            if (GetBoolAttr(this.currentConfig, "RemoveIntegerChecks"))
            {
            }

            if (GetBoolAttr(this.currentConfig, "TreatWarningsAsErrors"))
            {
                options.TreatWarningsAsErrors = true;
            }

            if (GetProjectProperty("WarningLevel", false) != null)
            {
                try
                {
                    options.WarningLevel = Int32.Parse(GetProjectProperty("WarningLevel", false), CultureInfo.InvariantCulture);
                }
                catch (ArgumentNullException e)
                {
                    Trace.WriteLine("Exception : " + e.Message);
                }
                catch (ArgumentException e)
                {
                    Trace.WriteLine("Exception : " + e.Message);
                }
                catch (FormatException e)
                {
                    Trace.WriteLine("Exception : " + e.Message);
                }
                catch (OverflowException e)
                {
                    Trace.WriteLine("Exception : " + e.Message);
                }
            }

            return options;
        }

        private string GetOutputPath(MSBuildExecution.ProjectInstance properties)
        {
            this.currentConfig = properties;
            string outputPath = GetProjectProperty("OutputPath");

            return outputPath;
        }

        private bool GetBoolAttr(MSBuildExecution.ProjectInstance properties, string name)
        {
            this.currentConfig = properties;
            string s = GetProjectProperty(name);

            return (s != null && s.ToUpperInvariant().Trim() == "TRUE");
        }

        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Attr")]
        [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "bool")]
        public virtual bool GetBoolAttr(string config, string platform, string name)
        {
            this.SetConfiguration(config, platform);
            return this.GetBoolAttr(this.currentConfig, name);
        }

        /// <summary>
        /// Get the assembly name for a give configuration
        /// </summary>
        /// <param name="config">the matching configuration in the msbuild file</param>
        /// <returns>assembly name</returns>
        public virtual string GetAssemblyName(string config, string platform)
        {
            this.SetConfiguration(config, platform);
            return GetAssemblyName(this.currentConfig);
        }

        /// <summary>
        /// Determines whether a file is a code file.
        /// </summary>
        /// <param name="fileName">Name of the file to be evaluated</param>
        /// <returns>false by default for any fileName</returns>
        public virtual bool IsCodeFile(string fileName)
        {
            return false;
        }

        public virtual string[] CodeFileExtensions {
            get {
                return new string[0];
            }
        }

        /// <summary>
        /// Determines whether the given file is a resource file (resx file).
        /// </summary>
        /// <param name="fileName">Name of the file to be evaluated.</param>
        /// <returns>true if the file is a resx file, otherwise false.</returns>
        public virtual bool IsEmbeddedResource(string fileName)
        {
            return String.Equals(Path.GetExtension(fileName), ".ResX", StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// Create a file node based on an msbuild item.
        /// </summary>
        /// <param name="item">msbuild item</param>
        /// <returns>FileNode added</returns>
        public abstract FileNode CreateFileNode(ProjectElement item);

        /// <summary>
        /// Create a file node based on a string.
        /// </summary>
        /// <param name="file">filename of the new filenode</param>
        /// <returns>File node added</returns>
        public abstract FileNode CreateFileNode(string file);

        /// <summary>
        /// Create dependent file node based on an msbuild item
        /// </summary>
        /// <param name="item">msbuild item</param>
        /// <returns>dependent file node</returns>
        public virtual DependentFileNode CreateDependentFileNode(MsBuildProjectElement item)
        {
            return new DependentFileNode(this, item);
        }

        /// <summary>
        /// Create a dependent file node based on a string.
        /// </summary>
        /// <param name="file">filename of the new dependent file node</param>
        /// <returns>Dependent node added</returns>
        public virtual DependentFileNode CreateDependentFileNode(string file)
        {
            var item = AddFileToMsBuild(file);
            return this.CreateDependentFileNode(item);
        }

        /// <summary>
        /// Walks the subpaths of a project relative path and checks if the folder nodes hierarchy is already there, if not creates it.
        /// </summary>
        /// <param name="strPath">Path of the folder, can be relative to project or absolute</param>
        public virtual HierarchyNode CreateFolderNodes(string path, bool createOnDisk = true)
        {
            VsUtilities.ArgumentNotNullOrEmpty("path", path);

            if (Path.IsPathRooted(path))
            {
                // Ensure we are using a path deeper than ProjectHome
                if (!CommonUtils.IsSubpathOf(ProjectHome, path))
                    throw new ArgumentException("The path is not within the project", "path");

                path = CommonUtils.GetRelativeDirectoryPath(ProjectHome, path);
            }

            // If the folder already exists, return early
            string strFullPath = CommonUtils.GetAbsoluteDirectoryPath(ProjectHome, path);
            uint uiItemId;
            if (ErrorHandler.Succeeded(ParseCanonicalName(strFullPath, out uiItemId)) && 
                uiItemId != 0)
            {
                var folder = this.NodeFromItemId(uiItemId) as FolderNode;
                if (folder != null)
                {
                    // found the folder, return immediately
                    return folder;
                }
            }

            
            string[] parts = path.Split(new [] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length == 0)
            {
                throw new ArgumentException("The path is invalid", "path");
            }
            path = parts[0];
            HierarchyNode curParent = VerifySubFolderExists(path, this, createOnDisk);

            // now we have an array of subparts....
            for (int i = 1; i < parts.Length; i++)
            {
                if (parts[i].Length > 0)
                {
                    path = Path.Combine(path, parts[i]);
                    curParent = VerifySubFolderExists(path, curParent, createOnDisk);
                }
            }
            return curParent;
        }

        /// <summary>
        /// Defines if Node has Designer. By default we do not support designers for nodes
        /// </summary>
        /// <param name="itemPath">Path to item to query for designer support</param>
        /// <returns>true if node has designer</returns>
        public virtual bool NodeHasDesigner(string itemPath)
        {
            return false;
        }

        /// <summary>
        /// List of Guids of the config independent property pages. It is called by the GetProperty for VSHPROPID_PropertyPagesCLSIDList property.
        /// </summary>
        /// <returns></returns>
        protected virtual Guid[] GetConfigurationIndependentPropertyPages()
        {
            return new Guid[0];
        }

        /// <summary>
        /// Returns a list of Guids of the configuration dependent property pages. It is called by the GetProperty for VSHPROPID_CfgPropertyPagesCLSIDList property.
        /// </summary>
        /// <returns></returns>
        protected virtual Guid[] GetConfigurationDependentPropertyPages()
        {
            return new Guid[0];
        }

        /// <summary>
        /// An ordered list of guids of the prefered property pages. See <see cref="__VSHPROPID.VSHPROPID_PriorityPropertyPagesCLSIDList"/>
        /// </summary>
        /// <returns>An array of guids.</returns>
        protected virtual Guid[] GetPriorityProjectDesignerPages()
        {
            return new Guid[] { Guid.Empty };
        }

        /// <summary>
        /// Takes a path and verifies that we have a node with that name.
        /// It is meant to be a helper method for CreateFolderNodes().
        /// For some scenario it may be useful to override.
        /// </summary>
        /// <param name="path">full path to the subfolder we want to verify.</param>
        /// <param name="parent">the parent node where to add the subfolder if it does not exist.</param>
        /// <returns>the foldernode correcsponding to the path.</returns>
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubFolder")]
        protected virtual FolderNode VerifySubFolderExists(string path, HierarchyNode parent, bool createOnDisk = true)
        {
            FolderNode folderNode = null;
            uint uiItemId;
            string strFullPath = CommonUtils.GetAbsoluteDirectoryPath(ProjectHome, path);
            if (ErrorHandler.Succeeded(this.ParseCanonicalName(strFullPath, out uiItemId)) &&
                uiItemId != 0)
            {
                Debug.Assert(this.NodeFromItemId(uiItemId) is FolderNode, "Not a FolderNode");
                folderNode = (FolderNode)this.NodeFromItemId(uiItemId);
            }

            if (folderNode == null && strFullPath != null && parent != null)
            {
                // folder does not exist yet...
                // We could be in the process of loading so see if msbuild knows about it
                ProjectElement item = null;
                foreach (MSBuild.ProjectItem folder in buildProject.GetItems(ProjectFileConstants.Folder))
                {
                    var absPath = CommonUtils.GetAbsoluteDirectoryPath(ProjectHome, folder.EvaluatedInclude);
                    if (CommonUtils.IsSameDirectory(absPath, strFullPath))
                    {
                        item = new MsBuildProjectElement(this, folder);
                        break;
                    }
                }
                // If MSBuild did not know about it, create a new one
                if (item == null) {
                    item = this.AddFolderToMsBuild(strFullPath);
                }
                if (createOnDisk) {
                    Directory.CreateDirectory(strFullPath);
                }
                folderNode = this.CreateFolderNode(item);
                parent.AddChild(folderNode);
            }

            return folderNode;
        }

        /// <summary>
        /// To support virtual folders, override this method to return your own folder nodes
        /// </summary>
        /// <param name="path">Path to store for this folder</param>
        /// <param name="element">Element corresponding to the folder</param>
        /// <returns>A FolderNode that can then be added to the hierarchy</returns>
        public virtual FolderNode CreateFolderNode(ProjectElement element)
        {
            return new FolderNode(this, element);
        }

        /// <summary>
        /// Gets the list of selected HierarchyNode objects
        /// </summary>
        /// <returns>A list of HierarchyNode objects</returns>
        public virtual IList<HierarchyNode> GetSelectedNodes()
        {
            // Retrieve shell interface in order to get current selection
            IVsMonitorSelection monitorSelection = this.GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection;
            VsUtilities.CheckNotNull(monitorSelection);

            List<HierarchyNode> selectedNodes = new List<HierarchyNode>();
            IntPtr hierarchyPtr = IntPtr.Zero;
            IntPtr selectionContainer = IntPtr.Zero;
            try
            {
                // Get the current project hierarchy, project item, and selection container for the current selection
                // If the selection spans multiple hierachies, hierarchyPtr is Zero
                uint itemid;
                IVsMultiItemSelect multiItemSelect = null;
                ErrorHandler.ThrowOnFailure(monitorSelection.GetCurrentSelection(out hierarchyPtr, out itemid, out multiItemSelect, out selectionContainer));

                // We only care if there are one ore more nodes selected in the tree
                if (itemid != VSConstants.VSITEMID_NIL && hierarchyPtr != IntPtr.Zero)
                {
                    IVsHierarchy hierarchy = Marshal.GetObjectForIUnknown(hierarchyPtr) as IVsHierarchy;

                    if (itemid != VSConstants.VSITEMID_SELECTION)
                    {
                        // This is a single selection. Compare hirarchy with our hierarchy and get node from itemid
                        if (VsUtilities.IsSameComObject(this, hierarchy))
                        {
                            HierarchyNode node = this.NodeFromItemId(itemid);
                            if (node != null)
                            {
                                selectedNodes.Add(node);
                            }
                        }
                    }
                    else if (multiItemSelect != null)
                    {
                        // This is a multiple item selection.

                        //Get number of items selected and also determine if the items are located in more than one hierarchy
                        uint numberOfSelectedItems;
                        int isSingleHierarchyInt;
                        ErrorHandler.ThrowOnFailure(multiItemSelect.GetSelectionInfo(out numberOfSelectedItems, out isSingleHierarchyInt));
                        bool isSingleHierarchy = (isSingleHierarchyInt != 0);

                        // Now loop all selected items and add to the list only those that are selected within this hierarchy
                        if (!isSingleHierarchy || (isSingleHierarchy && VsUtilities.IsSameComObject(this, hierarchy)))
                        {
                            Debug.Assert(numberOfSelectedItems > 0, "Bad number of selected itemd");
                            VSITEMSELECTION[] vsItemSelections = new VSITEMSELECTION[numberOfSelectedItems];
                            uint flags = (isSingleHierarchy) ? (uint)__VSGSIFLAGS.GSI_fOmitHierPtrs : 0;
                            ErrorHandler.ThrowOnFailure(multiItemSelect.GetSelectedItems(flags, numberOfSelectedItems, vsItemSelections));
                            foreach (VSITEMSELECTION vsItemSelection in vsItemSelections)
                            {
                                if (isSingleHierarchy || VsUtilities.IsSameComObject(this, vsItemSelection.pHier))
                                {
                                    HierarchyNode node = this.NodeFromItemId(vsItemSelection.itemid);
                                    if (node != null)
                                    {
                                        selectedNodes.Add(node);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            finally
            {
                if (hierarchyPtr != IntPtr.Zero)
                {
                    Marshal.Release(hierarchyPtr);
                }
                if (selectionContainer != IntPtr.Zero)
                {
                    Marshal.Release(selectionContainer);
                }
            }

            return selectedNodes;
        }

        /// <summary>
        /// Recursevily walks the hierarchy nodes and redraws the state icons
        /// </summary>
        public override void UpdateSccStateIcons()
        {
            if (this.FirstChild == null)
            {
                return;
            }

            for (HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling)
            {
                n.UpdateSccStateIcons();
            }
        }


        /// <summary>
        /// Handles the shows all objects command.
        /// </summary>
        /// <returns></returns>
        public virtual int ShowAllFiles()
        {
            return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
        }

        /// <summary>
        /// Unloads the project.
        /// </summary>
        /// <returns></returns>
        public virtual int UnloadProject()
        {
            return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
        }

        /// <summary>
        /// Handles the clean project command.
        /// </summary>
        /// <returns></returns>
        protected virtual int CleanProject()
        {
            return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
        }

        /// <summary>
        /// Reload project from project file
        /// </summary>
        protected virtual void Reload()
        {
            try
            {
                this.disableQueryEdit = true;

                this.isClosed = false;
                this.eventTriggeringFlag = ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents | ProjectNode.EventTriggering.DoNotTriggerTrackerEvents;

                LoadProjectFile(this.FileName);

                // Load the guid
                this.SetProjectGuidFromProjectFile();

                this.ProcessReferences();

                this.ProcessFiles();

                this.ProcessFolders();

                this.ProcessConfigurations();

                this.LoadNonBuildInformation();

                this.InitSccInfo();

                this.RegisterSccProject();
            }
            finally
            {
                this.SetProjectFileDirty(false);
                this.eventTriggeringFlag = ProjectNode.EventTriggering.TriggerAll;
                this.disableQueryEdit = false;
            }
        }

        protected virtual void ProcessConfigurations()
        {

        }

        protected virtual void LoadProjectFile(string filename)
        {
            Debug.Assert(this.buildEngine != null, "There is no build engine defined for this project");

            SetBuildProject(VsUtilities.ReinitializeMsBuildProject(this.buildEngine, this.filename, this.buildProject));
        }

        /// <summary>
        /// Renames the project file
        /// </summary>
        /// <param name="newFile">The full path of the new project file.</param>
        protected virtual void RenameProjectFile(string newFile)
        {
            IVsUIShell shell = this.Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
            Debug.Assert(shell != null, "Could not get the ui shell from the project");
            VsUtilities.CheckNotNull(shell);

            // Figure out what the new full name is
            string oldFile = this.Url;

            int canContinue = 0;
            IVsSolution vsSolution = (IVsSolution)this.GetService(typeof(SVsSolution));
            if (ErrorHandler.Succeeded(vsSolution.QueryRenameProject(this.GetOuterInterface<IVsProject>(), oldFile, newFile, 0, out canContinue))
                && canContinue != 0)
            {
                bool isFileSame = CommonUtils.IsSamePath(oldFile, newFile);

                // If file already exist and is not the same file with different casing
                if (!isFileSame && File.Exists(newFile))
                {
                    // Prompt the user for replace
                    string message = SR.GetString(SR.FileAlreadyExists, newFile);

                    if (!VsUtilities.IsInAutomationFunction(this.Site))
                    {
                        if (!VsShellUtilities.PromptYesNo(message, null, OLEMSGICON.OLEMSGICON_WARNING, shell))
                        {
                            throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(message);
                    }

                    // Delete the destination file after making sure it is not read only
                    File.SetAttributes(newFile, FileAttributes.Normal);
                    File.Delete(newFile);
                }

                SuspendFileChanges fileChanges = new SuspendFileChanges(this.Site, this.filename);
                fileChanges.Suspend();
                try
                {
                    // Actual file rename
                    this.SaveMSBuildProjectFileAs(newFile);

                    this.SetProjectFileDirty(false);

                    if (!isFileSame)
                    {
                        // Now that the new file name has been created delete the old one.
                        // TODO: Handle source control issues.
                        File.SetAttributes(oldFile, FileAttributes.Normal);
                        File.Delete(oldFile);
                    }

                    this.OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_Caption, 0);

                    // Update solution
                    ErrorHandler.ThrowOnFailure(vsSolution.OnAfterRenameProject((IVsProject)this, oldFile, newFile, 0));

                    ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0));
                }
                finally
                {
                    fileChanges.Resume();
                }
            }
            else
            {
                throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
            }
        }

        /// <summary>
        /// Called by the project to know if the item is a file (that is part of the project)
        /// or an intermediate file used by the MSBuild tasks/targets
        /// Override this method if your project has more types or different ones
        /// </summary>
        /// <param name="type">Type name</param>
        /// <returns>True = items of this type should be included in the project</returns>
        protected virtual bool IsItemTypeFileType(string type)
        {
            // recognize the typical types as a file....
            if (String.Compare(type, "Compile", StringComparison.OrdinalIgnoreCase) == 0
                || String.Compare(type, "Content", StringComparison.OrdinalIgnoreCase) == 0
                || String.Compare(type, "EmbeddedResource", StringComparison.OrdinalIgnoreCase) == 0
                || String.Compare(type, "None", StringComparison.OrdinalIgnoreCase) == 0)
                return true;

            // we don't know about this type, so ignore it.
            return false;
        }

        /// <summary>
        /// Filter items that should not be processed as file items. Example: Folders and References.
        /// </summary>
        protected virtual bool FilterItemTypeToBeAddedToHierarchy(string itemType)
        {
            return (String.Compare(itemType, ProjectFileConstants.Reference, StringComparison.OrdinalIgnoreCase) == 0
                    || String.Compare(itemType, ProjectFileConstants.ProjectReference, StringComparison.OrdinalIgnoreCase) == 0
                    || String.Compare(itemType, ProjectFileConstants.COMReference, StringComparison.OrdinalIgnoreCase) == 0
                    || String.Compare(itemType, ProjectFileConstants.Folder, StringComparison.OrdinalIgnoreCase) == 0
                    || String.Compare(itemType, ProjectFileConstants.WebReference, StringComparison.OrdinalIgnoreCase) == 0
                    || String.Compare(itemType, ProjectFileConstants.WebReferenceFolder, StringComparison.OrdinalIgnoreCase) == 0
                    || String.Compare(itemType, ProjectFileConstants.WebPiReference, StringComparison.OrdinalIgnoreCase) == 0);
        }


        /// <summary>
        /// Associate window output pane to the build logger
        /// </summary>
        /// <param name="output"></param>
        public virtual void SetOutputLogger(IVsOutputWindowPane output)
        {
            // Create our logger, if it was not specified
            if (!this.useProvidedLogger || this.buildLogger == null)
            {
                // Because we may be aggregated, we need to make sure to get the outer IVsHierarchy
                IntPtr unknown = IntPtr.Zero;
                IVsHierarchy hierarchy = null;
                try
                {
                    unknown = Marshal.GetIUnknownForObject(this);
                    hierarchy = Marshal.GetTypedObjectForIUnknown(unknown, typeof(IVsHierarchy)) as IVsHierarchy;
                }
                finally
                {
                    if (unknown != IntPtr.Zero)
                        Marshal.Release(unknown);
                }
                // Create the logger
                this.BuildLogger = new IDEBuildLogger(output, this.TaskProvider, hierarchy);

                // To retrive the verbosity level, the build logger depends on the registry root 
                // (otherwise it will used an hardcoded default)
                ILocalRegistry2 registry = this.GetService(typeof(SLocalRegistry)) as ILocalRegistry2;
                if (null != registry)
                {
                    string registryRoot;
                    ErrorHandler.ThrowOnFailure(registry.GetLocalRegistryRoot(out registryRoot));
                    IDEBuildLogger logger = this.BuildLogger as IDEBuildLogger;
                    if (!String.IsNullOrEmpty(registryRoot) && (null != logger))
                    {
                        logger.BuildVerbosityRegistryRoot = registryRoot;
                        logger.ErrorString = this.ErrorString;
                        logger.WarningString = this.WarningString;
                    }
                }
            }
            else
            {
                ((IDEBuildLogger)this.BuildLogger).OutputWindowPane = output;
            }
        }

        /// <summary>
        /// Set configuration properties for a specific configuration
        /// </summary>
        /// <param name="config">configuration name</param>
        protected virtual void SetBuildConfigurationProperties(string config, string platform)
        {
            CompilerParameters options = null;

            if (!String.IsNullOrEmpty(config) && !String.IsNullOrEmpty(platform))
            {
                options = this.GetProjectOptions(config, platform);
            }

            if (options != null && this.buildProject != null)
            {
                // Make sure the project configuration is set properly
                this.SetConfiguration(config, platform);
            }
        }


        /// <summary>
        /// This execute an MSBuild target for a design-time build.
        /// </summary>
        /// <param name="target">Name of the MSBuild target to execute</param>
        /// <returns>Result from executing the target (success/failure)</returns>
        /// <remarks>
        /// If you depend on the items/properties generated by the target
        /// you should be aware that any call to BuildTarget on any project
        /// will reset the list of generated items/properties
        /// </remarks>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
        public virtual MSBuildResult InvokeMsBuild(uint vsopts, string target, string config, string platform, IEnumerable<string> files)
        {
            MSBuildResult result = MSBuildResult.Failed;
            const bool designTime = true;
            bool requiresUIThread = UIThread.Instance.IsUIThread; // we don't run tasks that require calling the STA thread, so unless we're ON it, we don't need it.

            IVsBuildManagerAccessor accessor = this.Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor;
            BuildSubmission submission = null;

            try
            {
                // Do the actual Build
                if (this.buildProject != null)
                {
                    if (!TryBeginBuild(designTime, requiresUIThread))
                    {
                        throw new InvalidOperationException("A build is already in progress.");
                    }

                    string[] targetsToBuild = new string[target != null ? 1 : 0];
                    if (target != null)
                    {
                        targetsToBuild[0] = target;
                    }
                    
                    currentConfig = BuildProject.CreateProjectInstance();

                    BuildRequestData requestData = new BuildRequestData(currentConfig, targetsToBuild, this.BuildProject.ProjectCollection.HostServices, BuildRequestDataFlags.ReplaceExistingProjectInstance);
                    submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
                    if (accessor != null)
                    {
                        ErrorHandler.ThrowOnFailure(accessor.RegisterLogger(submission.SubmissionId, this.buildLogger));
                    }

                    BuildResult buildResult = submission.Execute();

                    result = (buildResult.OverallResult == BuildResultCode.Success) ? MSBuildResult.Successful : MSBuildResult.Failed;
                }
            }
            finally
            {
                EndBuild(vsopts, submission, designTime, requiresUIThread);
            }

            return result;
        }


        /// <summary>
        /// Initialize common project properties with default value if they are empty
        /// </summary>
        /// <remarks>The following common project properties are defaulted to projectName (if empty):
        ///    AssemblyName, Name and RootNamespace.
        /// If the project filename is not set then no properties are set</remarks>
        protected virtual void InitializeProjectProperties()
        {
            // Get projectName from project filename. Return if not set
            string projectName = Path.GetFileNameWithoutExtension(this.filename);
            if (String.IsNullOrEmpty(projectName))
            {
                return;
            }

            if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.AssemblyName)))
            {
                SetProjectProperty(ProjectFileConstants.AssemblyName, projectName);
            }
            if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.Name)))
            {
                SetProjectProperty(ProjectFileConstants.Name, projectName);
            }
            if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.RootNamespace)))
            {
                SetProjectProperty(ProjectFileConstants.RootNamespace, projectName);
            }
        }

        /// <summary>
        /// Factory method for configuration provider
        /// </summary>
        /// <returns>Configuration provider created</returns>
        protected abstract ConfigProvider CreateConfigProvider();

        /// <summary>
        /// Factory method for reference container node
        /// </summary>
        /// <returns>ReferenceContainerNode created</returns>
        protected virtual ReferenceContainerNode CreateReferenceContainerNode()
        {
            return new ReferenceContainerNode(this);
        }

        /// <summary>
        /// Saves the project file on a new name.
        /// </summary>
        /// <param name="newFileName">The new name of the project file.</param>
        /// <returns>Success value or an error code.</returns>
        protected virtual int SaveAs(string newFileName)
        {
            Debug.Assert(!String.IsNullOrEmpty(newFileName), "Cannot save project file for an empty or null file name");
            VsUtilities.ArgumentNotNullOrEmpty(newFileName, "newFileName");

            newFileName = newFileName.Trim();

            string errorMessage = String.Empty;

            if (newFileName.Length > NativeMethods.MAX_PATH)
            {
                errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), newFileName);
            }
            else
            {
                string fileName = String.Empty;

                try
                {
                    fileName = Path.GetFileNameWithoutExtension(newFileName);
                }
                // We want to be consistent in the error message and exception we throw. fileName could be for example #�&%"�&"%  and that would trigger an ArgumentException on Path.IsRooted.
                catch (ArgumentException)
                {
                    errorMessage = String.Format(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture), newFileName);
                }

                if (errorMessage.Length == 0)
                {
                    // If there is no filename or it starts with a leading dot issue an error message and quit.
                    // For some reason the save as dialog box allows to save files like "......ext"
                    if (String.IsNullOrEmpty(fileName) || fileName[0] == '.')
                    {
                        errorMessage = SR.GetString(SR.FileNameCannotContainALeadingPeriod, CultureInfo.CurrentUICulture);
                    }
                    else if (VsUtilities.ContainsInvalidFileNameChars(newFileName))
                    {
                        errorMessage = String.Format(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture), newFileName);
                    }
                }
            }
            if (errorMessage.Length > 0)
            {
                // If it is not called from an automation method show a dialog box.
                if (!VsUtilities.IsInAutomationFunction(this.Site))
                {
                    string title = null;
                    OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
                    OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
                    OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
                    VsShellUtilities.ShowMessageBox(this.Site, title, errorMessage, icon, buttons, defaultButton);
                    return VSConstants.OLE_E_PROMPTSAVECANCELLED;
                }

                throw new InvalidOperationException(errorMessage);
            }

            string oldName = this.filename;

            IVsSolution solution = this.Site.GetService(typeof(IVsSolution)) as IVsSolution;
            Debug.Assert(solution != null, "Could not retrieve the solution form the service provider");
            VsUtilities.CheckNotNull(solution);

            int canRenameContinue = 0;
            ErrorHandler.ThrowOnFailure(solution.QueryRenameProject(this.GetOuterInterface<IVsProject>(), this.filename, newFileName, 0, out canRenameContinue));

            if (canRenameContinue == 0)
            {
                return VSConstants.OLE_E_PROMPTSAVECANCELLED;
            }

            SuspendFileChanges fileChanges = new SuspendFileChanges(this.Site, oldName);
            fileChanges.Suspend();
            try
            {
                // Save the project file and project file related properties.
                this.SaveMSBuildProjectFileAs(newFileName);

                this.SetProjectFileDirty(false);

                // TODO: If source control is enabled check out the project file.

                //Redraw.
                this.OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_Caption, 0);

                ErrorHandler.ThrowOnFailure(solution.OnAfterRenameProject(this, oldName, this.filename, 0));

                IVsUIShell shell = this.Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
                Debug.Assert(shell != null, "Could not get the ui shell from the project");
                VsUtilities.CheckNotNull(shell);

                ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0));
            }
            finally
            {
                fileChanges.Resume();
            }

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Saves project file related information to the new file name. It also calls msbuild API to save the project file.
        /// It is called by the SaveAs method and the SetEditLabel before the project file rename related events are triggered. 
        /// An implementer can override this method to provide specialized semantics on how the project file is renamed in the msbuild file.
        /// </summary>
        /// <param name="newFileName">The new full path of the project file</param>
        protected virtual void SaveMSBuildProjectFileAs(string newFileName)
        {
            Debug.Assert(!String.IsNullOrEmpty(newFileName), "Cannot save project file for an empty or null file name");

            string newProjectHome = CommonUtils.GetRelativeDirectoryPath(Path.GetDirectoryName(newFileName), ProjectHome);
            this.buildProject.SetProperty(CommonConstants.ProjectHome, newProjectHome);

            this.buildProject.FullPath = newFileName;


            this.filename = newFileName;

            string newFileNameWithoutExtension = Path.GetFileNameWithoutExtension(newFileName);

            // Refresh solution explorer
            this.SetProjectProperty(ProjectFileConstants.Name, newFileNameWithoutExtension);

            // Saves the project file on disk.
            this.buildProject.Save(newFileName);

        }

        /// <summary>
        /// Adds a file to the msbuild project.
        /// </summary>
        /// <param name="file">The file to be added.</param>
        /// <returns>A Projectelement describing the newly added file.</returns>
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ToMs")]
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
        public virtual MsBuildProjectElement AddFileToMsBuild(string file)
        {
            MsBuildProjectElement newItem;

            string itemPath = CommonUtils.GetRelativeFilePath(ProjectHome, file);
            Debug.Assert(!Path.IsPathRooted(itemPath), "Cannot add item with full path.");

            if (this.IsCodeFile(itemPath))
            {
                newItem = this.CreateMsBuildFileItem(itemPath, ProjectFileConstants.Compile);
                newItem.SetMetadata(ProjectFileConstants.SubType, ProjectFileAttributeValue.Code);
            }
            else if (this.IsEmbeddedResource(itemPath))
            {
                newItem = this.CreateMsBuildFileItem(itemPath, ProjectFileConstants.EmbeddedResource);
            }
            else
            {
                newItem = this.CreateMsBuildFileItem(itemPath, ProjectFileConstants.Content);
                newItem.SetMetadata(ProjectFileConstants.SubType, ProjectFileConstants.Content);
            }

            return newItem;
        }

        /// <summary>
        /// Adds a folder to the msbuild project.
        /// </summary>
        /// <param name="folder">The folder to be added.</param>
        /// <returns>A ProjectElement describing the newly added folder.</returns>
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ToMs")]
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
        protected virtual ProjectElement AddFolderToMsBuild(string folder)
        {
            ProjectElement newItem;

            if (Path.IsPathRooted(folder))
            {
                folder = CommonUtils.GetRelativeDirectoryPath(ProjectHome, folder);
                Debug.Assert(!Path.IsPathRooted(folder), "Cannot add item with full path.");
            }

            newItem = this.CreateMsBuildFileItem(folder, ProjectFileConstants.Folder);

            return newItem;
        }

        const int E_CANCEL_FILE_ADD = unchecked((int)0xA0010001);      // Severity = Error, Customer Bit set, Facility = 1, Error = 1

        /// <summary>
        /// Checks to see if the user wants to overwrite the specified file name.  
        /// 
        /// Returns:
        ///     E_ABORT if we disallow the user to overwrite the file
        ///     OLECMDERR_E_CANCELED if the user wants to cancel
        ///     S_OK if the user wants to overwrite
        ///     E_CANCEL_FILE_ADD (0xA0010001) if the user doesn't want to overwrite and wants to abort the larger transaction
        /// </summary>
        /// <param name="originalFileName"></param>
        /// <param name="computedNewFileName"></param>
        /// <param name="canCancel"></param>
        /// <returns></returns>
        protected int CanOverwriteExistingItem(string originalFileName, string computedNewFileName, bool inProject = true)
        {
            if (String.IsNullOrEmpty(originalFileName) || String.IsNullOrEmpty(computedNewFileName))
            {
                return VSConstants.E_INVALIDARG;
            }

            string message = String.Empty;
            string title = String.Empty;
            OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
            OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
            OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;

            // If the document is open then return error message.
            IVsUIHierarchy hier;
            IVsWindowFrame windowFrame;
            uint itemid = VSConstants.VSITEMID_NIL;

            bool isOpen = VsShellUtilities.IsDocumentOpen(this.Site, computedNewFileName, Guid.Empty, out hier, out itemid, out windowFrame);

            if (isOpen)
            {
                message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.CannotAddFileThatIsOpenInEditor, CultureInfo.CurrentUICulture), Path.GetFileName(computedNewFileName));
                VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
                return VSConstants.E_ABORT;
            }


            // File already exists in project... message box
            message = String.Format(SR.GetString(inProject ? SR.FileAlreadyInProject : SR.FileAlreadyExists, CultureInfo.CurrentUICulture), Path.GetFileName(originalFileName));
            icon = OLEMSGICON.OLEMSGICON_QUERY;
            buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO;
            int msgboxResult = VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
            if (msgboxResult == NativeMethods.IDCANCEL)
            {
                return (int)E_CANCEL_FILE_ADD;
            }
            else if (msgboxResult != NativeMethods.IDYES)
            {
                return (int)OleConstants.OLECMDERR_E_CANCELED;
            }

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Adds a new file node to the hierarchy.
        /// </summary>
        /// <param name="parentNode">The parent of the new fileNode</param>
        /// <param name="fileName">The file name</param>
        protected virtual void AddNewFileNodeToHierarchy(HierarchyNode parentNode, string fileName)
        {
            VsUtilities.ArgumentNotNull("parentNode", parentNode);

            HierarchyNode child;

            // In the case of subitem, we want to create dependent file node
            // and set the DependentUpon property
            if (this.canFileNodesHaveChilds && (parentNode is FileNode || parentNode is DependentFileNode))
            {
                child = this.CreateDependentFileNode(fileName);
                child.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon, parentNode.ItemNode.GetMetadata(ProjectFileConstants.Include));

                // Make sure to set the HasNameRelation flag on the dependent node if it is related to the parent by name
                if (!child.HasParentNodeNameRelation && string.Compare(child.GetRelationalName(), parentNode.GetRelationalName(), StringComparison.OrdinalIgnoreCase) == 0)
                {
                    child.HasParentNodeNameRelation = true;
                }
            }
            else
            {
                //Create and add new filenode to the project
                child = this.CreateFileNode(fileName);
            }

            parentNode.AddChild(child);

            // TODO : Revisit the VSADDFILEFLAGS here. Can it be a nested project?
            this.tracker.OnItemAdded(fileName, VSADDFILEFLAGS.VSADDFILEFLAGS_NoFlags);
        }

        /// <summary>
        /// Defines whther the current mode of the project is in a supress command mode.
        /// </summary>
        /// <returns></returns>
        public virtual bool IsCurrentStateASuppressCommandsMode()
        {
            if (VsShellUtilities.IsSolutionBuilding(this.Site))
            {
                return true;
            }

            DBGMODE dbgMode = VsShellUtilities.GetDebugMode(this.Site) & ~DBGMODE.DBGMODE_EncMask;
            if (dbgMode == DBGMODE.DBGMODE_Run || dbgMode == DBGMODE.DBGMODE_Break)
            {
                return true;
            }

            return false;

        }


        /// <summary>
        /// This is the list of output groups that the configuration object should
        /// provide.
        /// The first string is the name of the group.
        /// The second string is the target name (MSBuild) for that group.
        /// 
        /// To add/remove OutputGroups, simply override this method and edit the list.
        /// 
        /// To get nice display names and description for your groups, override:
        ///        - GetOutputGroupDisplayName
        ///        - GetOutputGroupDescription
        /// </summary>
        /// <returns>List of output group name and corresponding MSBuild target</returns>
        public virtual IList<KeyValuePair<string, string>> GetOutputGroupNames()
        {
            return new List<KeyValuePair<string, string>>(outputGroupNames);
        }

        /// <summary>
        /// Get the display name of the given output group.
        /// </summary>
        /// <param name="canonicalName">Canonical name of the output group</param>
        /// <returns>Display name</returns>
        public virtual string GetOutputGroupDisplayName(string canonicalName)
        {
            string result = SR.GetString(String.Format(CultureInfo.InvariantCulture, "Output{0}", canonicalName), CultureInfo.CurrentUICulture);
            if (String.IsNullOrEmpty(result))
                result = canonicalName;
            return result;
        }

        /// <summary>
        /// Get the description of the given output group.
        /// </summary>
        /// <param name="canonicalName">Canonical name of the output group</param>
        /// <returns>Description</returns>
        public virtual string GetOutputGroupDescription(string canonicalName)
        {
            string result = SR.GetString(String.Format(CultureInfo.InvariantCulture, "Output{0}Description", canonicalName), CultureInfo.CurrentUICulture);
            if (String.IsNullOrEmpty(result))
                result = canonicalName;
            return result;
        }

        /// <summary>
        /// Set the configuration in MSBuild.
        /// This does not get persisted and is used to evaluate msbuild conditions
        /// which are based on the $(Configuration) property.
        /// </summary>
        public virtual void SetCurrentConfiguration()
        {
            // Can't ask for the active config until the project is opened, so do nothing in that scenario
            if (!this.projectOpened)
                return;

            EnvDTE.Project automationObject = this.GetAutomationObject() as EnvDTE.Project;

            this.SetConfiguration(VsUtilities.GetActiveConfigurationName(automationObject), VsUtilities.GetActivePlatformName(automationObject));
        }

        /// <summary>
        /// Set the configuration property in MSBuild.
        /// This does not get persisted and is used to evaluate msbuild conditions
        /// which are based on the $(Configuration) property.
        /// </summary>
        /// <param name="config">Configuration name</param>
        public virtual void SetConfiguration(string config, string platform)
        {
            VsUtilities.ArgumentNotNull("config", config);
            VsUtilities.ArgumentNotNull("platform", platform);

            // Can't ask for the active config until the project is opened, so do nothing in that scenario
            if (!projectOpened)
                return;


            bool propertiesChanged = this.buildProject.SetGlobalProperty(ProjectFileConstants.Configuration, config);
            if (this.buildProject.SetGlobalProperty(ProjectFileConstants.Platform, config))
            {
                propertiesChanged = true;
            }

            if (this.currentConfig == null || propertiesChanged)
            {
                this.currentConfig = this.buildProject.CreateProjectInstance();
            }
        }

        /// <summary>
        /// Loads reference items from the project file into the hierarchy.
        /// </summary>
        public virtual void ProcessReferences()
        {
            IReferenceContainer container = GetReferenceContainer();
            if (null == container)
            {
                // This project type does not support references or there is a problem
                // creating the reference container node.
                // In both cases there is no point to try to process references, so exit.
                return;
            }

            // Load the referernces.
            container.LoadReferencesFromBuildProject(buildProject);
        }

        /// <summary>
        /// Loads folders from the project file into the hierarchy.
        /// </summary>
        public virtual void ProcessFolders()
        {
            // Process Folders (useful to persist empty folder)
            foreach (MSBuild.ProjectItem folder in this.buildProject.GetItems(ProjectFileConstants.Folder))
            {
                string strPath = folder.EvaluatedInclude;

                // We do not need any special logic for assuring that a folder is only added once to the ui hierarchy.
                // The below method will only add once the folder to the ui hierarchy
                this.CreateFolderNodes(strPath, false);
            }
        }

        /// <summary>
        /// Loads file items from the project file into the hierarchy.
        /// </summary>
        public virtual void ProcessFiles()
        {
            List<String> subitemsKeys = new List<String>();
            Dictionary<String, MSBuild.ProjectItem> subitems = new Dictionary<String, MSBuild.ProjectItem>();

            // Define a set for our build items. The value does not really matter here.
            Dictionary<String, MSBuild.ProjectItem> items = new Dictionary<String, MSBuild.ProjectItem>();

            // Process Files
            foreach (MSBuild.ProjectItem item in this.buildProject.Items.ToArray()) // copy the array, we could add folders while enumerating
            {
                // Ignore items imported from .targets files. In particular, this will ignore the <Content>
                // items that are generated from any <Compile> items in our .targets.
                if (item.IsImported) {
                    continue;
                }

                // Ignore the item if it is a reference or folder
                if (this.FilterItemTypeToBeAddedToHierarchy(item.ItemType))
                    continue;

                // MSBuilds tasks/targets can create items (such as object files),
                // such items are not part of the project per say, and should not be displayed.
                // so ignore those items.
                if (!this.IsItemTypeFileType(item.ItemType))
                    continue;

                // If the item is already contained do nothing.
                // TODO: possibly report in the error list that the the item is already contained in the project file similar to Language projects.
                if (items.ContainsKey(item.EvaluatedInclude.ToUpperInvariant()))
                    continue;

                // Make sure that we do not want to add the item, dependent, or independent twice to the ui hierarchy
                items.Add(item.EvaluatedInclude.ToUpperInvariant(), item);


                string dependentOf = item.GetMetadataValue(ProjectFileConstants.DependentUpon);
                string link = item.GetMetadataValue(ProjectFileConstants.Link);
                if (!String.IsNullOrWhiteSpace(link))
                {
                    if (Path.IsPathRooted(link))
                    {
                        // ignore fully rooted link paths.
                        continue;
                    }

                    if (!Path.IsPathRooted(item.EvaluatedInclude))
                    {
                        var itemPath = CommonUtils.GetAbsoluteFilePath(ProjectHome, item.EvaluatedInclude);
                        if (CommonUtils.IsSubpathOf(ProjectHome, itemPath))
                        {
                            // linked file which lives in our directory, don't allow that.
                            continue;
                        }
                    }

                    var linkPath = CommonUtils.GetAbsoluteFilePath(ProjectHome, link);
                    if (!CommonUtils.IsSubpathOf(ProjectHome, linkPath))
                    {
                        // relative path outside of project, don't allow that.
                        continue;
                    }
                }

                if (!this.CanFileNodesHaveChilds || String.IsNullOrEmpty(dependentOf))
                {
                    var parent = GetItemParentNode(item);
                    string filename = Path.GetFileName(item.EvaluatedInclude);
                    HierarchyNode existingChild = null;
                    for (HierarchyNode child = parent.FirstChild; child != null; child = child.NextSibling)
                    {
                        if (String.Equals(Path.GetFileName(child.Url), filename, StringComparison.OrdinalIgnoreCase))
                        {
                            existingChild = child;
                            break;
                        }
                    }

                    if (existingChild != null)
                    {
                        if (existingChild.IsLinkFile)
                        {
                            // remove link node.
                            existingChild.Parent.RemoveChild(existingChild);
                        }
                        else
                        {
                            // we have duplicate entries, or this is a link file.
                            continue;
                        }
                    }

                    AddIndependentFileNode(item, parent);
                }
                else
                {
                    // We will process dependent items later.
                    // Note that we use 2 lists as we want to remove elements from
                    // the collection as we loop through it
                    subitemsKeys.Add(item.EvaluatedInclude);
                    subitems.Add(item.EvaluatedInclude, item);
                }
            }

            // Now process the dependent items.
            if (this.CanFileNodesHaveChilds)
            {
                ProcessDependentFileNodes(subitemsKeys, subitems);
            }

        }

        /// <summary>
        /// Processes dependent filenodes from list of subitems. Multi level supported, but not circular dependencies.
        /// </summary>
        /// <param name="subitemsKeys">List of sub item keys </param>
        /// <param name="subitems"></param>
        public virtual void ProcessDependentFileNodes(IList<String> subitemsKeys, Dictionary<String, MSBuild.ProjectItem> subitems)
        {
            if (subitemsKeys == null || subitems == null)
            {
                return;
            }

            foreach (string key in subitemsKeys)
            {
                // A previous pass could have removed the key so make sure it still needs to be added
                if (!subitems.ContainsKey(key))
                    continue;

                AddDependentFileNode(subitems, key);
            }
        }

        /// <summary>
        /// For flavored projects which implement IPersistXMLFragment, load the information now
        /// </summary>
        public virtual void LoadNonBuildInformation()
        {
            IPersistXMLFragment outerHierarchy = GetOuterInterface<IPersistXMLFragment>();
            if (outerHierarchy != null)
            {
                this.LoadXmlFragment(outerHierarchy, null, null);
            }
        }

        /// <summary>
        /// Used to sort nodes in the hierarchy.
        /// </summary>
        public virtual int CompareNodes(HierarchyNode node1, HierarchyNode node2)
        {
            Debug.Assert(node1 != null && node2 != null);
            VsUtilities.ArgumentNotNull("node1", node1);
            VsUtilities.ArgumentNotNull("node2", node2);

            if (node1.SortPriority == node2.SortPriority)
            {
                return String.Compare(node2.Caption, node1.Caption, true, CultureInfo.CurrentCulture);
            }
            else
            {
                return node2.SortPriority - node1.SortPriority;
            }
        }

        #endregion

        #region non-virtual methods

        public void InstantiateItemsDraggedOrCutOrCopiedList() {
            itemsDraggedOrCutOrCopied = new List<HierarchyNode>();
        }

        /// <summary>
        /// Overloaded method. Invokes MSBuild using the default configuration and does without logging on the output window pane.
        /// </summary>
        public MSBuildResult Build(string target)
        {
            return this.Build(0, String.Empty, string.Empty, null, target);
        }

        /// <summary>
        /// This is called from the main thread before the background build starts.
        ///  cleanBuild is not part of the vsopts, but passed down as the callpath is differently
        ///  PrepareBuild mainly creates directories and cleans house if cleanBuild is true
        /// </summary>
        public virtual void PrepareBuild(uint vsopts, string config, string platform, bool cleanBuild) {
            if (this.buildIsPrepared && !cleanBuild) return;

            string outputPath = GetProjectProperty("OutputPath");

            if (string.IsNullOrWhiteSpace(outputPath)) return;


            outputPath = Path.GetDirectoryName(outputPath);

            if (cleanBuild && this.currentConfig.Targets.ContainsKey(MsBuildTarget.Clean)) {
                this.InvokeMsBuild(0, MsBuildTarget.Clean, config, platform, null);
            }

            PackageUtilities.EnsureOutputPath(outputPath);

            this.buildIsPrepared = true;
        }

        /// <summary>
        /// Do the build by invoking msbuild
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "vsopts")]
        public virtual MSBuildResult Build(uint vsopts, string config, string platform, IVsOutputWindowPane output, string target)
        {
            lock (ProjectNode.BuildLock)
            {
                bool engineLogOnlyCritical = this.BuildPrelude(output);

                MSBuildResult result = MSBuildResult.Failed;

                try
                {
                    this.SetBuildConfigurationProperties(config, platform);

                    result = this.InvokeMsBuild(vsopts, target, config, platform, null);
                }
                finally
                {
                    // Unless someone specifically request to use an output window pane, we should not output to it
                    if (null != output)
                    {
                        this.SetOutputLogger(null);
                        BuildEngine.OnlyLogCriticalEvents = engineLogOnlyCritical;
                    }
                }

                return result;
            }
        }

        /// <summary>
        /// Get value of Project property
        /// </summary>
        /// <param name="propertyName">Name of Property to retrieve</param>
        /// <returns>Value of property</returns>
        public string GetProjectProperty(string propertyName)
        {
            return this.GetProjectProperty(propertyName, true);
        }

        /// <summary>
        /// Set dirty state of project
        /// </summary>
        /// <param name="value">boolean value indicating dirty state</param>
        public void SetProjectFileDirty(bool value)
        {
            this.isDirty = value;
            if (this.isDirty)
            {
                this.lastModifiedTime = DateTime.Now;
                this.buildIsPrepared = false;
            }
        }

        /// <summary>
        /// Get Node from ItemID.
        /// </summary>
        /// <param name="itemId">ItemID for the requested node</param>
        /// <returns>Node if found</returns>
        public HierarchyNode NodeFromItemId(uint itemId)
        {
            if (VSConstants.VSITEMID_ROOT == itemId)
            {
                return this;
            }
            else if (VSConstants.VSITEMID_NIL == itemId)
            {
                return null;
            }
            else if (VSConstants.VSITEMID_SELECTION == itemId)
            {
                throw new NotImplementedException();
            }

            return (HierarchyNode)this.ItemIdMap[itemId];
        }

        /// <summary>
        /// This method return new project element, and add new MSBuild item to the project/build hierarchy
        /// </summary>
        /// <param name="file">file name</param>
        /// <param name="itemType">MSBuild item type</param>
        /// <returns>new project element</returns>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")]
        public MsBuildProjectElement CreateMsBuildFileItem(string file, string itemType)
        {
            return new MsBuildProjectElement(this, file, itemType);
        }

        /// <summary>
        /// This method returns new project element based on existing MSBuild item. It does not modify/add project/build hierarchy at all.
        /// </summary>
        /// <param name="item">MSBuild item instance</param>
        /// <returns>wrapping project element</returns>
        public MsBuildProjectElement GetProjectElement(MSBuild.ProjectItem item)
        {
            return new MsBuildProjectElement(this, item);
        }

        /// <summary>
        /// Create FolderNode from Path
        /// </summary>
        /// <param name="path">Path to folder</param>
        /// <returns>FolderNode created that can be added to the hierarchy</returns>
        public FolderNode CreateFolderNode(string path)
        {
            ProjectElement item = this.AddFolderToMsBuild(path);
            FolderNode folderNode = CreateFolderNode(item);
            return folderNode;
        }

        /// <summary>
        /// Verify if the file can be written to.
        /// Return false if the file is read only and/or not checked out
        /// and the user did not give permission to change it.
        /// Note that exact behavior can also be affected based on the SCC
        /// settings under Tools->Options.
        /// </summary>
        public bool QueryEditProjectFile(bool suppressUI)
        {
            bool result = true;
            if (this.site == null)
            {
                // We're already zombied. Better return FALSE.
                result = false;
            }
            else if (this.disableQueryEdit)
            {
                return true;
            }
            else
            {
                IVsQueryEditQuerySave2 queryEditQuerySave = this.GetService(typeof(SVsQueryEditQuerySave)) as IVsQueryEditQuerySave2;
                if (queryEditQuerySave != null)
                {   // Project path dependends on server/client project
                    string path = this.filename;

                    tagVSQueryEditFlags qef = tagVSQueryEditFlags.QEF_AllowInMemoryEdits;
                    if (suppressUI)
                        qef |= tagVSQueryEditFlags.QEF_SilentMode;

                    // If we are debugging, we want to prevent our project from being reloaded. To 
                    // do this, we pass the QEF_NoReload flag
                    if (!VsUtilities.IsVisualStudioInDesignMode(this.Site))
                        qef |= tagVSQueryEditFlags.QEF_NoReload;

                    uint verdict;
                    uint moreInfo;
                    string[] files = new string[1];
                    files[0] = path;
                    uint[] flags = new uint[1];
                    VSQEQS_FILE_ATTRIBUTE_DATA[] attributes = new VSQEQS_FILE_ATTRIBUTE_DATA[1];
                    int hr = queryEditQuerySave.QueryEditFiles(
                                    (uint)qef,
                                    1, // 1 file
                                    files, // array of files
                                    flags, // no per file flags
                                    attributes, // no per file file attributes
                                    out verdict,
                                    out moreInfo /* ignore additional results */);

                    tagVSQueryEditResult qer = (tagVSQueryEditResult)verdict;
                    if (ErrorHandler.Failed(hr) || (qer != tagVSQueryEditResult.QER_EditOK))
                    {
                        if (!suppressUI && !VsUtilities.IsInAutomationFunction(this.Site))
                        {
                            string message = SR.GetString(SR.CancelQueryEdit, path);
                            string title = string.Empty;
                            OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
                            OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
                            OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
                            VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
                        }
                        result = false;
                    }
                }
            }
            return result;
        }

        public bool QueryFolderAdd(HierarchyNode targetFolder, string path)
        {
            if (!disableQueryEdit)
            {
                var queryTrack = this.GetService(typeof(SVsTrackProjectDocuments)) as IVsTrackProjectDocuments2;
                if (queryTrack != null)
                {
                    VSQUERYADDDIRECTORYRESULTS[] res = new VSQUERYADDDIRECTORYRESULTS[1];
                    ErrorHandler.ThrowOnFailure(
                        queryTrack.OnQueryAddDirectories(
                            GetOuterInterface<IVsProject>(),
                            1,
                            new[] { CommonUtils.GetAbsoluteFilePath(GetBaseDirectoryForAddingFiles(targetFolder), Path.GetFileName(path)) },
                            new[] { VSQUERYADDDIRECTORYFLAGS.VSQUERYADDDIRECTORYFLAGS_padding },
                            res,
                            res
                        )
                    );

                    if (res[0] == VSQUERYADDDIRECTORYRESULTS.VSQUERYADDDIRECTORYRESULTS_AddNotOK)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        public bool QueryFolderRemove(HierarchyNode targetFolder, string path) {
            if (!disableQueryEdit) 
            {
                var queryTrack = this.GetService(typeof(SVsTrackProjectDocuments)) as IVsTrackProjectDocuments2;
                if (queryTrack != null) 
                {
                    VSQUERYREMOVEDIRECTORYRESULTS[] res = new VSQUERYREMOVEDIRECTORYRESULTS[1];
                    ErrorHandler.ThrowOnFailure(
                        queryTrack.OnQueryRemoveDirectories(
                            GetOuterInterface<IVsProject>(),
                            1,
                            new[] { CommonUtils.GetAbsoluteFilePath(GetBaseDirectoryForAddingFiles(targetFolder), Path.GetFileName(path)) },
                            new[] { VSQUERYREMOVEDIRECTORYFLAGS.VSQUERYREMOVEDIRECTORYFLAGS_padding },
                            res,
                            res
                        )
                    );

                    if (res[0] == VSQUERYREMOVEDIRECTORYRESULTS.VSQUERYREMOVEDIRECTORYRESULTS_RemoveNotOK) 
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// Given a node determines what is the directory that can accept files.
        /// If the node is a FoldeNode than it is the Url of the Folder.
        /// If the node is a ProjectNode it is the project folder.
        /// Otherwise (such as FileNode subitem) it delegate the resolution to the parent node.
        /// </summary>
        public string GetBaseDirectoryForAddingFiles(HierarchyNode nodeToAddFile)
        {
            string baseDir = String.Empty;

            if (nodeToAddFile is FolderNode)
            {
                baseDir = nodeToAddFile.Url;
            }
            else if (nodeToAddFile is ProjectNode)
            {
                baseDir = this.ProjectHome;
            }
            else if (nodeToAddFile != null)
            {
                baseDir = GetBaseDirectoryForAddingFiles(nodeToAddFile.Parent);
            }

            return baseDir;
        }

        /// <summary>
        /// For public use only.
        /// This creates a copy of an existing configuration and add it to the project.
        /// Caller should change the condition on the PropertyGroup.
        /// If derived class want to accomplish this, they should call ConfigProvider.AddCfgsOfCfgName()
        /// It is expected that in the future MSBuild will have support for this so we don't have to
        /// do it manually.
        /// </summary>
        /// <param name="group">PropertyGroup to clone</param>
        /// <returns></returns>
        public MSBuildConstruction.ProjectPropertyGroupElement ClonePropertyGroup(MSBuildConstruction.ProjectPropertyGroupElement group)
        {
            // Create a new (empty) PropertyGroup
            MSBuildConstruction.ProjectPropertyGroupElement newPropertyGroup = this.buildProject.Xml.AddPropertyGroup();

            // Now copy everything from the group we are trying to clone to the group we are creating
            if (!String.IsNullOrEmpty(group.Condition))
                newPropertyGroup.Condition = group.Condition;
            foreach (MSBuildConstruction.ProjectPropertyElement prop in group.Properties)
            {
                MSBuildConstruction.ProjectPropertyElement newProperty = newPropertyGroup.AddProperty(prop.Name, prop.Value);
                if (!String.IsNullOrEmpty(prop.Condition))
                    newProperty.Condition = prop.Condition;
            }

            return newPropertyGroup;
        }

        /// <summary>
        /// Get the project extensions
        /// </summary>
        /// <returns></returns>
        public MSBuildConstruction.ProjectExtensionsElement GetProjectExtensions()
        {
            var extensionsElement = this.buildProject.Xml.ChildrenReversed.OfType<MSBuildConstruction.ProjectExtensionsElement>().FirstOrDefault();

            if (extensionsElement == null)
            {
                extensionsElement = this.buildProject.Xml.CreateProjectExtensionsElement();
                this.buildProject.Xml.AppendChild(extensionsElement);
            }

            return extensionsElement;
        }

        /// <summary>
        /// Set the xmlText as a project extension element with the id passed.
        /// </summary>
        /// <param name="id">The id of the project extension element.</param>
        /// <param name="xmlText">The value to set for a project extension.</param>
        public void SetProjectExtensions(string id, string xmlText)
        {
            MSBuildConstruction.ProjectExtensionsElement element = this.GetProjectExtensions();

            // If it doesn't already have a value and we're asked to set it to
            // nothing, don't do anything. Same as old OM. Keeps project neat.
            if (element == null)
            {
                if (xmlText.Length == 0)
                {
                    return;
                }

                element = this.buildProject.Xml.CreateProjectExtensionsElement();
                this.buildProject.Xml.AppendChild(element);
            }

            element[id] = xmlText;
        }

        /// <summary>
        /// Register the project with the Scc manager.
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
        protected void RegisterSccProject()
        {

            if (this.isRegisteredWithScc || String.IsNullOrEmpty(this.sccProjectName))
            {
                return;
            }

            IVsSccManager2 sccManager = this.Site.GetService(typeof(SVsSccManager)) as IVsSccManager2;

            if (sccManager != null)
            {
                ErrorHandler.ThrowOnFailure(sccManager.RegisterSccProject(this, this.sccProjectName, this.sccAuxPath, this.sccLocalPath, this.sccProvider));

                this.isRegisteredWithScc = true;
            }
        }

        /// <summary>
        ///  Unregisters us from the SCC manager
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "UnRegister")]
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Un")]
        protected void UnRegisterProject()
        {
            if (!this.isRegisteredWithScc)
            {
                return;
            }

            IVsSccManager2 sccManager = this.Site.GetService(typeof(SVsSccManager)) as IVsSccManager2;

            if (sccManager != null)
            {
                ErrorHandler.ThrowOnFailure(sccManager.UnregisterSccProject(this));
                this.isRegisteredWithScc = false;
            }
        }

        /// <summary>
        /// Get the CATID corresponding to the specified type.
        /// </summary>
        /// <param name="type">Type of the object for which you want the CATID</param>
        /// <returns>CATID</returns>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CATID")]
        public Guid GetCATIDForType(Type type)
        {
            VsUtilities.ArgumentNotNull("type", type);

            if (catidMapping.ContainsKey(type))
                return catidMapping[type];
            // If you get here and you want your object to be extensible, then add a call to AddCATIDMapping() in your project constructor
            return Guid.Empty;
        }

        /// <summary>
        /// This is used to specify a CATID corresponding to a BrowseObject or an ExtObject.
        /// The CATID can be any GUID you choose. For types which are your owns, you could use
        /// their type GUID, while for other types (such as those provided in the MPF) you should
        /// provide a different GUID.
        /// </summary>
        /// <param name="type">Type of the extensible object</param>
        /// <param name="catid">GUID that extender can use to uniquely identify your object type</param>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "catid")]
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CATID")]
        protected void AddCATIDMapping(Type type, Guid catid)
        {
            catidMapping.Add(type, catid);
        }

        /// <summary>
        /// Initialize an object with an XML fragment.
        /// </summary>
        /// <param name="iPersistXMLFragment">Object that support being initialized with an XML fragment</param>
        /// <param name="configName">Name of the configuration being initialized, null if it is the project</param>
        public void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName, string platformName)
        {
            VsUtilities.ArgumentNotNull("persistXmlFragment", persistXmlFragment);

            if (xmlFragments == null)
            {
                // Retrieve the xml fragments from MSBuild
                xmlFragments = new XmlDocument();

                string fragments = GetProjectExtensions()[ProjectFileConstants.VisualStudio];
                fragments = String.Format(CultureInfo.InvariantCulture, "<root>{0}</root>", fragments);
                xmlFragments.LoadXml(fragments);
            }

            // We need to loop through all the flavors
            string flavorsGuid;
            ErrorHandler.ThrowOnFailure(((IVsAggregatableProject)this).GetAggregateProjectTypeGuids(out flavorsGuid));
            foreach (Guid flavor in VsUtilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(flavorsGuid))
            {
                // Look for a matching fragment
                string flavorGuidString = flavor.ToString("B");
                string fragment = null;
                XmlNode node = null;
                foreach (XmlNode child in xmlFragments.FirstChild.ChildNodes)
                {
                    if (child.Attributes.Count > 0)
                    {
                        //TODO: check for platformName
                        string guid = String.Empty;
                        string configuration = String.Empty;
                        if (child.Attributes[ProjectFileConstants.Guid] != null)
                            guid = child.Attributes[ProjectFileConstants.Guid].Value;
                        if (child.Attributes[ProjectFileConstants.Configuration] != null)
                            configuration = child.Attributes[ProjectFileConstants.Configuration].Value;

                        if (String.Compare(child.Name, ProjectFileConstants.FlavorProperties, StringComparison.OrdinalIgnoreCase) == 0
                                && String.Compare(guid, flavorGuidString, StringComparison.OrdinalIgnoreCase) == 0
                                && ((String.IsNullOrEmpty(configName) && String.IsNullOrEmpty(configuration))
                                    || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0)))
                        {
                            // we found the matching fragment
                            fragment = child.InnerXml;
                            node = child;
                            break;
                        }
                    }
                }

                Guid flavorGuid = flavor;
                if (String.IsNullOrEmpty(fragment))
                {
                    // the fragment was not found so init with default values
                    ErrorHandler.ThrowOnFailure(persistXmlFragment.InitNew(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE));
                    // While we don't yet support user files, our flavors might, so we will store that in the project file until then
                    // TODO: Refactor this code when we support user files
                    ErrorHandler.ThrowOnFailure(persistXmlFragment.InitNew(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE));
                }
                else
                {
                    ErrorHandler.ThrowOnFailure(persistXmlFragment.Load(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE, fragment));
                    // While we don't yet support user files, our flavors might, so we will store that in the project file until then
                    // TODO: Refactor this code when we support user files
                    if (node.NextSibling != null && node.NextSibling.Attributes[ProjectFileConstants.User] != null)
                        ErrorHandler.ThrowOnFailure(persistXmlFragment.Load(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE, node.NextSibling.InnerXml));
                }
            }
        }

        /// <summary>
        /// Retrieve all XML fragments that need to be saved from the flavors and store the information in msbuild.
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")]
        protected void PersistXMLFragments()
        {
            if (this.IsFlavorDirty() != 0)
            {
                XmlDocument doc = new XmlDocument();
                XmlElement root = doc.CreateElement("ROOT");

                // We will need the list of configuration inside the loop, so get it before entering the loop
                uint[] count = new uint[1];
                IVsCfg[] configs = null;
                int hr = this.ConfigProvider.GetCfgs(0, null, count, null);
                if (ErrorHandler.Succeeded(hr) && count[0] > 0)
                {
                    configs = new IVsCfg[count[0]];
                    hr = this.ConfigProvider.GetCfgs((uint)configs.Length, configs, count, null);
                    if (ErrorHandler.Failed(hr))
                        count[0] = 0;
                }
                if (count[0] == 0)
                    configs = new IVsCfg[0];

                // We need to loop through all the flavors
                string flavorsGuid;
                ErrorHandler.ThrowOnFailure(((IVsAggregatableProject)this).GetAggregateProjectTypeGuids(out flavorsGuid));
                foreach (Guid flavor in VsUtilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(flavorsGuid))
                {
                    IPersistXMLFragment outerHierarchy = GetOuterInterface<IPersistXMLFragment>();
                    // First check the project
                    if (outerHierarchy != null)
                    {
                        // Retrieve the XML fragment
                        string fragment = string.Empty;
                        Guid flavorGuid = flavor;
                        ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE, out fragment, 1));
                        if (!String.IsNullOrEmpty(fragment))
                        {
                            // Add the fragment to our XML
                            WrapXmlFragment(doc, root, flavor, null, fragment);
                        }
                        // While we don't yet support user files, our flavors might, so we will store that in the project file until then
                        // TODO: Refactor this code when we support user files
                        fragment = String.Empty;
                        ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE, out fragment, 1));
                        if (!String.IsNullOrEmpty(fragment))
                        {
                            // Add the fragment to our XML
                            XmlElement node = WrapXmlFragment(doc, root, flavor, null, fragment);
                            node.Attributes.Append(doc.CreateAttribute(ProjectFileConstants.User));
                        }
                    }

                    // Then look at the configurations
                    foreach (IVsCfg config in configs)
                    {
                        // Get the fragment for this flavor/config pair
                        string fragment;
                        ErrorHandler.ThrowOnFailure(((Config)config).GetXmlFragment(flavor, _PersistStorageType.PST_PROJECT_FILE, out fragment));
                        if (!String.IsNullOrEmpty(fragment))
                        {
                            WrapXmlFragment(doc, root, flavor, ((Config)config).ConfigName, fragment);
                        }
                    }
                }
                if (root.ChildNodes != null && root.ChildNodes.Count > 0)
                {
                    // Save our XML (this is only the non-build information for each flavor) in msbuild
                    SetProjectExtensions(ProjectFileConstants.VisualStudio, root.InnerXml.ToString());
                }
            }
        }

        #endregion

        #region IVsGetCfgProvider Members
        //=================================================================================

        public virtual int GetCfgProvider(out IVsCfgProvider p)
        {
            // Be sure to call the property here since that is doing a polymorhic ProjectConfig creation.
            p = this.ConfigProvider;
            return (p == null ? VSConstants.E_NOTIMPL : VSConstants.S_OK);
        }
        #endregion

        #region IPersist Members

        public int GetClassID(out Guid clsid)
        {
            clsid = this.ProjectGuid;
            return VSConstants.S_OK;
        }
        #endregion

        #region IPersistFileFormat Members

        int IPersistFileFormat.GetClassID(out Guid clsid)
        {
            clsid = this.ProjectGuid;
            return VSConstants.S_OK;
        }

        public virtual int GetCurFile(out string name, out uint formatIndex)
        {
            name = this.filename;
            formatIndex = 0;
            return VSConstants.S_OK;
        }

        public virtual int GetFormatList(out string formatlist)
        {
            formatlist = String.Empty;
            return VSConstants.S_OK;
        }

        public virtual int InitNew(uint formatIndex)
        {
            return VSConstants.S_OK;
        }

        public virtual int IsDirty(out int isDirty)
        {
            isDirty = 0;
            if (this.buildProject.Xml.HasUnsavedChanges || this.IsProjectFileDirty)
            {
                isDirty = 1;
                return VSConstants.S_OK;
            }

            isDirty = IsFlavorDirty();

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Get the outer IVsHierarchy implementation.
        /// This is used for scenario where a flavor may be modifying the behavior
        /// </summary>
        public IVsHierarchy GetOuterHierarchy() {
            IVsHierarchy hierarchy = null;
            // The hierarchy of a node is its project node hierarchy
            IntPtr projectUnknown = Marshal.GetIUnknownForObject(this);
            try {
                hierarchy = (IVsHierarchy)Marshal.GetTypedObjectForIUnknown(projectUnknown, typeof(IVsHierarchy));
            } finally {
                if (projectUnknown != IntPtr.Zero) {
                    Marshal.Release(projectUnknown);
                }
            }
            return hierarchy;
        }

        public T GetOuterInterface<T>() where T : class {
            return GetOuterHierarchy() as T;
        }

        protected int IsFlavorDirty()
        {
            int isDirty = 0;
            // See if one of our flavor consider us dirty
            IPersistXMLFragment outerHierarchy = GetOuterInterface<IPersistXMLFragment>();
            if (outerHierarchy != null)
            {
                // First check the project
                ErrorHandler.ThrowOnFailure(outerHierarchy.IsFragmentDirty((uint)_PersistStorageType.PST_PROJECT_FILE, out isDirty));
                // While we don't yet support user files, our flavors might, so we will store that in the project file until then
                // TODO: Refactor this code when we support user files
                if (isDirty == 0)
                    ErrorHandler.ThrowOnFailure(outerHierarchy.IsFragmentDirty((uint)_PersistStorageType.PST_USER_FILE, out isDirty));
            }
            if (isDirty == 0)
            {
                // Then look at the configurations
                uint[] count = new uint[1];
                int hr = this.ConfigProvider.GetCfgs(0, null, count, null);
                if (ErrorHandler.Succeeded(hr) && count[0] > 0)
                {
                    // We need to loop through the configurations
                    IVsCfg[] configs = new IVsCfg[count[0]];
                    hr = this.ConfigProvider.GetCfgs((uint)configs.Length, configs, count, null);
                    Debug.Assert(ErrorHandler.Succeeded(hr), "failed to retrieve configurations");
                    foreach (IVsCfg config in configs)
                    {
                        isDirty = ((Config)config).IsFlavorDirty(_PersistStorageType.PST_PROJECT_FILE);
                        if (isDirty != 0)
                            break;
                    }
                }
            }
            return isDirty;
        }

        public virtual int Load(string fileName, uint mode, int readOnly)
        {
            this.filename = fileName;
            this.Reload();
            return VSConstants.S_OK;
        }

        public virtual int Save(string fileToBeSaved, int remember, uint formatIndex)
        {
            // The file name can be null. Then try to use the Url.
            string tempFileToBeSaved = fileToBeSaved;
            if (String.IsNullOrEmpty(tempFileToBeSaved) && !String.IsNullOrEmpty(this.Url))
            {
                tempFileToBeSaved = this.Url;
            }

            if (String.IsNullOrEmpty(tempFileToBeSaved))
            {
                throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "fileToBeSaved");
            }

            bool setProjectFileDirtyAfterSave = false;
            if (remember == 0)
            {
                setProjectFileDirtyAfterSave = this.IsProjectFileDirty;
            }

            // Update the project with the latest flavor data (if needed)
            PersistXMLFragments();

            int result = VSConstants.S_OK;
            bool saveAs = true;
            if (CommonUtils.IsSamePath(tempFileToBeSaved, this.filename))
            {
                saveAs = false;
            }
            if (!saveAs)
            {
                SuspendFileChanges fileChanges = new SuspendFileChanges(this.Site, this.filename);
                fileChanges.Suspend();
                try
                {
                    // Ensure the directory exist
                    string saveFolder = Path.GetDirectoryName(tempFileToBeSaved);
                    if (!Directory.Exists(saveFolder))
                        Directory.CreateDirectory(saveFolder);
                    // Save the project
                    SaveMSBuildProjectFile(tempFileToBeSaved);
                    this.SetProjectFileDirty(false);
                }
                finally
                {
                    fileChanges.Resume();
                }
            }
            else
            {
                result = this.SaveAs(tempFileToBeSaved);
                if (result != VSConstants.OLE_E_PROMPTSAVECANCELLED)
                {
                    ErrorHandler.ThrowOnFailure(result);
                }

            }

            if (setProjectFileDirtyAfterSave)
            {
                this.SetProjectFileDirty(true);
            }

            return result;
        }

        protected virtual void SaveMSBuildProjectFile(string filename) 
        {
            buildProject.Save(filename);
        }

        public virtual int SaveCompleted(string filename)
        {
            // TODO: turn file watcher back on.
            return VSConstants.S_OK;
        }
        #endregion

        #region IVsProject3 Members

        /// <summary>
        /// Callback from the additem dialog. Deals with adding new and existing items
        /// </summary>
        public virtual int GetMkDocument(uint itemId, out string mkDoc)
        {
            mkDoc = null;
            if (itemId == VSConstants.VSITEMID_SELECTION)
            {
                return VSConstants.E_UNEXPECTED;
            }

            HierarchyNode n = this.NodeFromItemId(itemId);
            if (n == null)
            {
                return VSConstants.E_INVALIDARG;
            }

            mkDoc = n.GetMkDocument();

            if (String.IsNullOrEmpty(mkDoc))
            {
                return VSConstants.E_FAIL;
            }

            return VSConstants.S_OK;
        }


        public virtual int AddItem(uint itemIdLoc, VSADDITEMOPERATION op, string itemName, uint filesToOpen, string[] files, IntPtr dlgOwner, VSADDRESULT[] result)
        {
            Guid empty = Guid.Empty;

            return AddItemWithSpecific(itemIdLoc, op, itemName, filesToOpen, files, dlgOwner, 0, ref empty, null, ref empty, result);
        }

        /// <summary>
        /// Creates new items in a project, adds existing files to a project, or causes Add Item wizards to be run
        /// </summary>
        /// <param name="itemIdLoc"></param>
        /// <param name="op"></param>
        /// <param name="itemName"></param>
        /// <param name="filesToOpen"></param>
        /// <param name="files">Array of file names. 
        /// If dwAddItemOperation is VSADDITEMOP_CLONEFILE the first item in the array is the name of the file to clone. 
        /// If dwAddItemOperation is VSADDITEMOP_OPENDIRECTORY, the first item in the array is the directory to open. 
        /// If dwAddItemOperation is VSADDITEMOP_RUNWIZARD, the first item is the name of the wizard to run, 
        /// and the second item is the file name the user supplied (same as itemName).</param>
        /// <param name="dlgOwner"></param>
        /// <param name="editorFlags"></param>
        /// <param name="editorType"></param>
        /// <param name="physicalView"></param>
        /// <param name="logicalView"></param>
        /// <param name="result"></param>
        /// <returns>S_OK if it succeeds </returns>
        /// <remarks>The result array is initalized to failure.</remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        public virtual int AddItemWithSpecific(uint itemIdLoc, VSADDITEMOPERATION op, string itemName, uint filesToOpen, string[] files, IntPtr dlgOwner, uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, VSADDRESULT[] result)
        {
            return AddItemWithSpecificInternal(itemIdLoc, op, itemName, filesToOpen, files, dlgOwner, editorFlags, ref editorType, physicalView, ref logicalView, result);
        }

        // TODO: Refactor me into something sane
        public int AddItemWithSpecificInternal(uint itemIdLoc, VSADDITEMOPERATION op, string itemName, uint filesToOpen, string[] files, IntPtr dlgOwner, uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, VSADDRESULT[] result, bool? promptOverwrite = null)
        {
            if (files == null || result == null || files.Length == 0 || result.Length == 0)
            {
                return VSConstants.E_INVALIDARG;
            }

            // Locate the node to be the container node for the file(s) being added
            // only projectnode or foldernode and file nodes are valid container nodes
            // We need to locate the parent since the item wizard expects the parent to be passed.
            HierarchyNode n = this.NodeFromItemId(itemIdLoc);
            if (n == null)
            {
                return VSConstants.E_INVALIDARG;
            }

            while (!n.CanAddFiles && (!this.CanFileNodesHaveChilds || !(n is FileNode)))
            {
                n = n.Parent;
            }
            Debug.Assert(n != null, "We should at this point have either a ProjectNode or FolderNode or a FileNode as a container for the new filenodes");

            // handle link and runwizard operations at this point
            bool isLink = false;
            switch (op)
            {
                case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE:
                    // we do not support this right now
                    isLink = true;
                    break;

                case VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD:
                    result[0] = this.RunWizard(n, itemName, files[0], dlgOwner);
                    return VSConstants.S_OK;
            }

            string[] actualFiles = new string[files.Length];


            VSQUERYADDFILEFLAGS[] flags = this.GetQueryAddFileFlags(files);

            string baseDir = this.GetBaseDirectoryForAddingFiles(n);
            // If we did not get a directory for node that is the parent of the item then fail.
            if (String.IsNullOrEmpty(baseDir))
            {
                return VSConstants.E_FAIL;
            }

            // Pre-calculates some paths that we can use when calling CanAddItems
            List<string> filesToAdd = new List<string>();
            for (int index = 0; index < files.Length; index++)
            {
                string newFileName = String.Empty;

                string file = files[index];

                switch (op)
                {
                    case VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE:
                        {
                            string fileName = Path.GetFileName(itemName ?? file);
                            newFileName = CommonUtils.GetAbsoluteFilePath(baseDir, fileName);
                        }
                        break;
                    case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE:
                    case VSADDITEMOPERATION.VSADDITEMOP_OPENFILE:
                        {
                            string fileName = Path.GetFileName(file);
                            newFileName = CommonUtils.GetAbsoluteFilePath(baseDir, fileName);

                            var friendlyPath = CommonUtils.CreateFriendlyFilePath(ProjectHome, file);

                            if (isLink && CommonUtils.IsSubpathOf(ProjectHome, file))
                            {
                                // creating a link to a file that's actually in the project, it's not really a link.
                                isLink = false;
                                newFileName = file;
                                n = this.CreateFolderNodes(Path.GetDirectoryName(file));
                            }
                        }
                        break;
                }
                filesToAdd.Add(newFileName);
            }

            // Ask tracker objects if we can add files
            if (!this.tracker.CanAddItems(filesToAdd.ToArray(), flags))
            {
                // We were not allowed to add the files
                return VSConstants.E_FAIL;
            }

            if (!this.QueryEditProjectFile(false))
            {
                throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
            }

            // Add the files to the hierarchy
            int actualFilesAddedIndex = 0;
            for (int index = 0; index < filesToAdd.Count; index++)
            {
                HierarchyNode child;
                bool overwrite = false;
                MsBuildProjectElement linkedFile = null;
                string newFileName = filesToAdd[index];

                string file = files[index];
                result[0] = VSADDRESULT.ADDRESULT_Failure;

                child = this.FindNodeByFullPath(newFileName);
                if (child != null)
                {
                    // If the file to be added is an existing file part of the hierarchy then continue.
                    if (CommonUtils.IsSamePath(file, newFileName))
                    {
                        if (child.IsNonMemberItem) 
                        {
                            // https://pytools.codeplex.com/workitem/1251
                            ErrorHandler.ThrowOnFailure(child.IncludeInProject(false));
                        }
                        result[0] = VSADDRESULT.ADDRESULT_Cancel;
                        continue;
                    }
                    else if (isLink)
                    {
                        string message = "There is already a file of the same name in this folder.";
                        string title = string.Empty;
                        OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
                        OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
                        OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;

                        VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);

                        result[0] = VSADDRESULT.ADDRESULT_Cancel;
                        return (int)OleConstants.OLECMDERR_E_CANCELED;
                    }
                    else
                    {
                        int canOverWriteExistingItem = CanOverwriteExistingItem(file, newFileName, !child.IsNonMemberItem);
                        if (canOverWriteExistingItem == E_CANCEL_FILE_ADD)
                        {
                            result[0] = VSADDRESULT.ADDRESULT_Cancel;
                            return (int)OleConstants.OLECMDERR_E_CANCELED;
                        }
                        else if (canOverWriteExistingItem == (int)OleConstants.OLECMDERR_E_CANCELED)
                        {
                            result[0] = VSADDRESULT.ADDRESULT_Cancel;
                            return canOverWriteExistingItem;
                        }
                        else if (canOverWriteExistingItem == VSConstants.S_OK)
                        {
                            overwrite = true;
                        }
                        else
                        {
                            return canOverWriteExistingItem;
                        }
                    }
                }
                else
                {
                    if (isLink)
                    {
                        child = this.FindNodeByFullPath(file);
                        if (child != null)
                        {
                            string message = String.Format("There is already a link to '{0}'. A project cannot have more than one link to the same file.", file);
                            string title = string.Empty;
                            OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
                            OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
                            OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;

                            VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);

                            result[0] = VSADDRESULT.ADDRESULT_Cancel;
                            return (int)OleConstants.OLECMDERR_E_CANCELED;
                        }
                    }

                    if (newFileName.Length >= NativeMethods.MAX_PATH) {
                        OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
                        OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
                        OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;

                        VsShellUtilities.ShowMessageBox(Site, FolderNode.PathTooLongMessage, null, icon, buttons, defaultButton);

                        result[0] = VSADDRESULT.ADDRESULT_Cancel;
                        return (int)OleConstants.OLECMDERR_E_CANCELED;
                    }

                    // we need to figure out where this file would be added and make sure there's
                    // not an existing link node at the same location
                    string filename = Path.GetFileName(newFileName);
                    var folder = this.FindNodeByFullPath(Path.GetDirectoryName(newFileName));
                    if (folder != null)
                    {
                        if (folder.FindImmediateChildByName(filename) != null) {
                                string message = "There is already a file of the same name in this folder.";
                                string title = string.Empty;
                                OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
                                OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
                                OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;

                                VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);

                                result[0] = VSADDRESULT.ADDRESULT_Cancel;
                                return (int)OleConstants.OLECMDERR_E_CANCELED;
                            }
                        }
                    }

                // If the file to be added is not in the same path copy it.
                if (!CommonUtils.IsSamePath(file, newFileName) || Directory.Exists(newFileName))
                {
                    if (!overwrite && File.Exists(newFileName))
                    {
                        var existingChild = this.FindNodeByFullPath(file);
                        if (existingChild == null || !existingChild.IsLinkFile)
                        {
                            string message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileAlreadyExists, CultureInfo.CurrentUICulture), newFileName);
                            string title = string.Empty;
                            OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY;
                            OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO;
                            OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
                            if (isLink)
                            {
                                message = "There is already a file of the same name in this folder.";
                                buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
                            }

                            int messageboxResult = VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
                            if (messageboxResult != NativeMethods.IDYES)
                            {
                                result[0] = VSADDRESULT.ADDRESULT_Cancel;
                                return (int)OleConstants.OLECMDERR_E_CANCELED;
                            }
                        }
                    }

                    var updatingNode = this.FindNodeByFullPath(file);
                    if (updatingNode != null && updatingNode.IsLinkFile)
                    {
                        // we just need to update the link to the new path.
                        linkedFile = updatingNode.ItemNode as MsBuildProjectElement;
                    }
                    else if (Directory.Exists(file))
                    {
                        // http://pytools.codeplex.com/workitem/546

                        int hr = AddDirectory(result, n, file, promptOverwrite);
                        if (ErrorHandler.Failed(hr)) {
                            return hr;
                        }
                        result[0] = VSADDRESULT.ADDRESULT_Success;
                        continue;
                    }
                    else if (!isLink)
                    {
                        // Copy the file to the correct location.
                        // We will suppress the file change events to be triggered to this item, since we are going to copy over the existing file and thus we will trigger a file change event. 
                        // We do not want the filechange event to ocur in this case, similar that we do not want a file change event to occur when saving a file.
                        IVsFileChangeEx fileChange = this.site.GetService(typeof(SVsFileChangeEx)) as IVsFileChangeEx;
                        VsUtilities.CheckNotNull(fileChange);

                        try
                        {
                            ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 1));
                            if (op == VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE)
                            {
                                this.AddFileFromTemplate(file, newFileName);
                            }
                            else
                            {
                                PackageUtilities.CopyUrlToLocal(new Uri(file), newFileName);

                                // Reset RO attribute on file if present - for example, if source file was under TFS control and not checked out.
                                try {
                                    var fileInfo = new FileInfo(newFileName);
                                    if (fileInfo.Attributes.HasFlag(FileAttributes.ReadOnly)) {
                                        fileInfo.Attributes &= ~FileAttributes.ReadOnly;
                                    }
                                } catch (Exception) {
                                    // Best-effort, but no big deal if this fails.
                                }
                            }
                        }
                        finally
                        {
                            ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 0));
                        }
                    }
                }

                if (overwrite)
                {
                    if (child.IsNonMemberItem) 
                    {
                        ErrorHandler.ThrowOnFailure(child.IncludeInProject(false));
                    }
                }
                else if (linkedFile != null || isLink)
                {
                    // files not moving, add the old name, and set the link.
                    var friendlyPath = CommonUtils.GetRelativeFilePath(ProjectHome, file);
                    FileNode newChild;
                    if (linkedFile == null)
                    {
                        Debug.Assert(!CommonUtils.IsSubpathOf(ProjectHome, file), "Should have cleared isLink above for file in project dir");
                        newChild = CreateFileNode(file);
                    }
                    else
                    {
                        newChild = CreateFileNode(linkedFile);
                    }

                    newChild.SetIsLinkFile(true);
                    newChild.ItemNode.SetMetadata(ProjectFileConstants.Link, CommonUtils.CreateFriendlyFilePath(ProjectFolder, newFileName));
                    n.AddChild(newChild);

                    DocumentManager.RenameDocument(site, file, file, n.ID);

                    LinkFileAdded(file);

                    SetProjectFileDirty(true);
                }
                else
                {
                    //Add new filenode/dependentfilenode
                    this.AddNewFileNodeToHierarchy(n, newFileName);
                }

                result[0] = VSADDRESULT.ADDRESULT_Success;
                actualFiles[actualFilesAddedIndex++] = newFileName;
            }

            // Notify listeners that items were appended.
            if (actualFilesAddedIndex > 0)
                OnItemsAppended(n);

            //Open files if this was requested through the editorFlags
            bool openFiles = (editorFlags & (uint)__VSSPECIFICEDITORFLAGS.VSSPECIFICEDITOR_DoOpen) != 0;
            if (openFiles && actualFiles.Length <= filesToOpen)
            {
                for (int i = 0; i < filesToOpen; i++)
                {
                    if (!String.IsNullOrEmpty(actualFiles[i]))
                    {
                        string name = actualFiles[i];
                        HierarchyNode child = this.FindNodeByFullPath(name);
                        Debug.Assert(child != null, "We should have been able to find the new element in the hierarchy");
                        if (child != null)
                        {
                            IVsWindowFrame frame;
                            if (editorType == Guid.Empty)
                            {
                                Guid view = Guid.Empty;
                                ErrorHandler.ThrowOnFailure(this.OpenItem(child.ID, ref view, IntPtr.Zero, out frame));
                            }
                            else
                            {
                                ErrorHandler.ThrowOnFailure(this.OpenItemWithSpecific(child.ID, editorFlags, ref editorType, physicalView, ref logicalView, IntPtr.Zero, out frame));
                            }

                            // Show the window frame in the UI and make it the active window
                            if (frame != null)
                            {
                                ErrorHandler.ThrowOnFailure(frame.Show());
                            }
                        }
                    }
                }
            }

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Adds a folder into the project recursing and adding any sub-files and sub-directories.
        /// 
        /// The user can be prompted to overwrite the existing files if the folder already exists
        /// in the project.  They will be initially prompted to overwrite - if they answer no
        /// we'll set promptOverwrite to false and when we recurse we won't prompt.  If they say
        /// yes then we'll set it to true and we will prompt for individual files.  
        /// </summary>
        private int AddDirectory(VSADDRESULT[] result, HierarchyNode n, string file, bool? promptOverwrite)
        {
            // need to recursively add all of the directory contents

            HierarchyNode targetFolder = n.FindImmediateChildByName(Path.GetFileName(file));
            if (targetFolder == null)
            {
                var fullPath = Path.Combine(GetBaseDirectoryForAddingFiles(n), Path.GetFileName(file));
                Directory.CreateDirectory(fullPath);
                var newChild = CreateFolderNode(fullPath);
                n.AddChild(newChild);
                targetFolder = newChild;
            } 
            else if (targetFolder.IsNonMemberItem) 
            {
                int hr = targetFolder.IncludeInProject(true);
                if (ErrorHandler.Failed(hr))
                {
                    return hr;
                }
                OnItemAdded(targetFolder.Parent, targetFolder);
                return hr;
            } 
            else if (promptOverwrite == null) 
            {
                var res = MessageBox.Show(
                    String.Format(
                    @"This folder already contains a folder called '{0}'.

If the files in the existing folder have the same names as files in the folder you are copying, do you want to replace the existing files?", Path.GetFileName(file)),
                    "Merge Folders",
                    MessageBoxButtons.YesNoCancel
                );

                // yes means prompt for each file
                // no means don't prompt for any of the files
                // cancel means forget what I'm doing

                switch (res) {
                    case DialogResult.Cancel:
                        result[0] = VSADDRESULT.ADDRESULT_Cancel;
                        return (int)OleConstants.OLECMDERR_E_CANCELED;
                    case DialogResult.No:
                        promptOverwrite = false;
                        break;
                    case DialogResult.Yes:
                        promptOverwrite = true;
                        break;
                }
            }

            Guid empty = Guid.Empty;

            // add the files...
            var dirFiles = Directory.GetFiles(file);
            if (dirFiles.Length > 0) {
            var subRes = AddItemWithSpecificInternal(
                targetFolder.ID,
                VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE,
                null,
                (uint)dirFiles.Length,
                dirFiles,
                IntPtr.Zero,
                0,
                ref empty,
                null,
                ref empty,
                result,
                promptOverwrite: promptOverwrite
            );

                if (ErrorHandler.Failed(subRes)) {
                return subRes;
            }
            }

            // add any subdirectories...

            var subDirs = Directory.GetDirectories(file);
            if (subDirs.Length > 0) {
            return AddItemWithSpecificInternal(
                targetFolder.ID,
                VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE,
                null,
                (uint)subDirs.Length,
                subDirs,
                IntPtr.Zero,
                0,
                ref empty,
                null,
                ref empty,
                result,
                promptOverwrite: promptOverwrite
            );
        }
            return VSConstants.S_OK;
        }

        protected virtual void LinkFileAdded(string filename)
        {
        }

        private static string GetIncrementedFileName(string newFileName, int count)
        {
            return CommonUtils.GetAbsoluteFilePath(Path.GetDirectoryName(newFileName), Path.GetFileNameWithoutExtension(newFileName) + " - Copy (" + count + ")" + Path.GetExtension(newFileName));
        }

        /// <summary>
        /// for now used by add folder. Called on the ROOT, as only the project should need
        /// to implement this.
        /// for folders, called with parent folder, blank extension and blank suggested root
        /// </summary>
        public virtual int GenerateUniqueItemName(uint itemIdLoc, string ext, string suggestedRoot, out string itemName)
        {
            string rootName = String.Empty;
            string extToUse = String.Empty;
            itemName = String.Empty;

            //force new items to have a number
            int cb = 1;
            bool found = false;
            bool fFolderCase = false;
            HierarchyNode parent = this.NodeFromItemId(itemIdLoc);

            if (!String.IsNullOrEmpty(ext))
            {
                extToUse = ext.Trim();
            }

            if (!String.IsNullOrEmpty(suggestedRoot))
            {
                suggestedRoot = suggestedRoot.Trim();
            }

            if (suggestedRoot == null || suggestedRoot.Length == 0)
            {
                // foldercase, we assume... 
                suggestedRoot = "NewFolder";
                fFolderCase = true;
            }

            while (!found)
            {
                rootName = suggestedRoot;
                if (cb > 0)
                    rootName += cb.ToString(CultureInfo.CurrentCulture);

                if (extToUse.Length > 0)
                {
                    rootName += extToUse;
                }

                cb++;
                found = true;
                for (HierarchyNode n = parent.FirstChild; n != null; n = n.NextSibling)
                {
                    if (rootName == n.GetEditLabel())
                    {
                        found = false;
                        break;
                    }

                    //if parent is a folder, we need the whole url
                    string parentFolder = parent.Url;
                    ProjectNode parentProj = parent as ProjectNode;
                    if (parentProj != null)
                        parentFolder = parentProj.ProjectHome;

                    string checkFile = CommonUtils.GetAbsoluteFilePath(parentFolder, rootName);

                    if (fFolderCase)
                    {
                        if (Directory.Exists(checkFile))
                        {
                            found = false;
                            break;
                        }
                    }
                    else
                    {
                        if (File.Exists(checkFile))
                        {
                            found = false;
                            break;
                        }
                    }
                }
            }

            itemName = rootName;
            return VSConstants.S_OK;
        }


        public virtual int GetItemContext(uint itemId, out Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp)
        {
            psp = null;
            HierarchyNode child = this.NodeFromItemId(itemId);
            if (child != null)
            {
                psp = child.OleServiceProvider as IOleServiceProvider;
            }
            return VSConstants.S_OK;
        }


        public virtual int IsDocumentInProject(string mkDoc, out int found, VSDOCUMENTPRIORITY[] pri, out uint itemId)
        {
            if (pri != null && pri.Length >= 1)
            {
                pri[0] = VSDOCUMENTPRIORITY.DP_Unsupported;
            }
            found = 0;
            itemId = 0;

            // If it is the project file just return.
            if (CommonUtils.IsSamePath(mkDoc, this.GetMkDocument()))
            {
                found = 1;
                itemId = VSConstants.VSITEMID_ROOT;
            }
            else
            {
                HierarchyNode child = this.FindNodeByFullPath(EnsureRootedPath(mkDoc));
                if (child != null)
                {
                    found = 1;
                    itemId = child.ID;
                }
            }

            if (found == 1)
            {
                if (pri != null && pri.Length >= 1)
                {
                    pri[0] = VSDOCUMENTPRIORITY.DP_Standard;
                }
            }

            return VSConstants.S_OK;

        }


        public virtual int OpenItem(uint itemId, ref Guid logicalView, IntPtr punkDocDataExisting, out IVsWindowFrame frame)
        {
            // Init output params
            frame = null;

            HierarchyNode n = this.NodeFromItemId(itemId);
            if (n == null)
            {
                throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture), "itemId");
            }

            // Delegate to the document manager object that knows how to open the item
            DocumentManager documentManager = n.GetDocumentManager();
            if (documentManager != null)
            {
                return documentManager.Open(ref logicalView, punkDocDataExisting, out frame, WindowFrameShowAction.DoNotShow);
            }

            // This node does not have an associated document manager and we must fail
            return VSConstants.E_FAIL;
        }


        public virtual int OpenItemWithSpecific(uint itemId, uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame frame)
        {
            // Init output params
            frame = null;

            HierarchyNode n = this.NodeFromItemId(itemId);
            if (n == null)
            {
                throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture), "itemId");
            }

            // Delegate to the document manager object that knows how to open the item
            DocumentManager documentManager = n.GetDocumentManager();
            if (documentManager != null)
            {
                return documentManager.OpenWithSpecific(editorFlags, ref editorType, physicalView, ref logicalView, docDataExisting, out frame, WindowFrameShowAction.DoNotShow);
            }

            // This node does not have an associated document manager and we must fail
            return VSConstants.E_FAIL;
        }


        public virtual int RemoveItem(uint reserved, uint itemId, out int result)
        {
            HierarchyNode n = this.NodeFromItemId(itemId);
            if (n == null)
            {
                throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture), "itemId");
            }
            n.Remove(true);
            result = 1;
            return VSConstants.S_OK;
        }


        public virtual int ReopenItem(uint itemId, ref Guid editorType, string physicalView, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame frame)
        {
            // Init output params
            frame = null;

            HierarchyNode n = this.NodeFromItemId(itemId);
            if (n == null)
            {
                throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture), "itemId");
            }

            // Delegate to the document manager object that knows how to open the item
            DocumentManager documentManager = n.GetDocumentManager();
            if (documentManager != null)
            {
                return documentManager.ReOpenWithSpecific(0, ref editorType, physicalView, ref logicalView, docDataExisting, out frame, WindowFrameShowAction.DoNotShow);
            }

            // This node does not have an associated document manager and we must fail
            return VSConstants.E_FAIL;
        }


        /// <summary>
        /// Implements IVsProject3::TransferItem
        /// This function is called when an open miscellaneous file is being transferred
        /// to our project. The sequence is for the shell to call AddItemWithSpecific and
        /// then use TransferItem to transfer the open document to our project.
        /// </summary>
        /// <param name="oldMkDoc">Old document name</param>
        /// <param name="newMkDoc">New document name</param>
        /// <param name="frame">Optional frame if the document is open</param>
        /// <returns></returns>
        public virtual int TransferItem(string oldMkDoc, string newMkDoc, IVsWindowFrame frame)
        {
            // Fail if hierarchy already closed
            if (this.ProjectMgr == null || this.IsClosed)
            {
                return VSConstants.E_FAIL;
            }
            //Fail if the document names passed are null.
            if (oldMkDoc == null || newMkDoc == null)
                return VSConstants.E_INVALIDARG;

            int hr = VSConstants.S_OK;
            VSDOCUMENTPRIORITY[] priority = new VSDOCUMENTPRIORITY[1];
            uint itemid = VSConstants.VSITEMID_NIL;
            uint cookie = 0;
            uint grfFlags = 0;

            IVsRunningDocumentTable pRdt = GetService(typeof(IVsRunningDocumentTable)) as IVsRunningDocumentTable;
            if (pRdt == null)
                return VSConstants.E_ABORT;

            string doc;
            int found;
            IVsHierarchy pHier;
            uint id, readLocks, editLocks;
            IntPtr docdataForCookiePtr = IntPtr.Zero;
            IntPtr docDataPtr = IntPtr.Zero;
            IntPtr hierPtr = IntPtr.Zero;

            // We get the document from the running doc table so that we can see if it is transient
            try
            {
                ErrorHandler.ThrowOnFailure(pRdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, oldMkDoc, out pHier, out id, out docdataForCookiePtr, out cookie));
            }
            finally
            {
                if (docdataForCookiePtr != IntPtr.Zero)
                    Marshal.Release(docdataForCookiePtr);
            }

            //Get the document info
            try
            {
                ErrorHandler.ThrowOnFailure(pRdt.GetDocumentInfo(cookie, out grfFlags, out readLocks, out editLocks, out doc, out pHier, out id, out docDataPtr));
            }
            finally
            {
                if (docDataPtr != IntPtr.Zero)
                    Marshal.Release(docDataPtr);
            }

            // Now see if the document is in the project. If not, we fail
            try
            {
                ErrorHandler.ThrowOnFailure(IsDocumentInProject(newMkDoc, out found, priority, out itemid));
                Debug.Assert(itemid != VSConstants.VSITEMID_NIL && itemid != VSConstants.VSITEMID_ROOT);
                hierPtr = Marshal.GetComInterfaceForObject(this, typeof(IVsUIHierarchy));
                // Now rename the document
                ErrorHandler.ThrowOnFailure(pRdt.RenameDocument(oldMkDoc, newMkDoc, hierPtr, itemid));
            }
            finally
            {
                if (hierPtr != IntPtr.Zero)
                    Marshal.Release(hierPtr);
            }

            //Change the caption if we are passed a window frame
            if (frame != null)
            {
                string caption = ((HierarchyNode)pHier).Caption;
                hr = frame.SetProperty((int)(__VSFPROPID.VSFPROPID_OwnerCaption), caption);
            }
            return hr;
        }

        #endregion

        #region IVsDependencyProvider Members
        public int EnumDependencies(out IVsEnumDependencies enumDependencies)
        {
            enumDependencies = new EnumDependencies(this.buildDependencyList);
            return VSConstants.S_OK;
        }

        public int OpenDependency(string szDependencyCanonicalName, out IVsDependency dependency)
        {
            dependency = null;
            return VSConstants.S_OK;
        }

        #endregion

        #region IVsComponentUser methods

        /// <summary>
        /// Add Components to the Project.
        /// Used by the environment to add components specified by the user in the Component Selector dialog 
        /// to the specified project
        /// </summary>
        /// <param name="dwAddCompOperation">The component operation to be performed.</param>
        /// <param name="cComponents">Number of components to be added</param>
        /// <param name="rgpcsdComponents">array of component selector data</param>
        /// <param name="hwndDialog">Handle to the component picker dialog</param>
        /// <param name="pResult">Result to be returned to the caller</param>
        public virtual int AddComponent(VSADDCOMPOPERATION dwAddCompOperation, uint cComponents, System.IntPtr[] rgpcsdComponents, System.IntPtr hwndDialog, VSADDCOMPRESULT[] pResult)
        {
            if (rgpcsdComponents == null || pResult == null)
            {
                return VSConstants.E_FAIL;
            }

            //initalize the out parameter
            pResult[0] = VSADDCOMPRESULT.ADDCOMPRESULT_Success;

            IReferenceContainer references = GetReferenceContainer();
            if (null == references)
            {
                // This project does not support references or the reference container was not created.
                // In both cases this operation is not supported.
                return VSConstants.E_NOTIMPL;
            }
            for (int cCount = 0; cCount < cComponents; cCount++)
            {
                VSCOMPONENTSELECTORDATA selectorData = new VSCOMPONENTSELECTORDATA();
                IntPtr ptr = rgpcsdComponents[cCount];
                selectorData = (VSCOMPONENTSELECTORDATA)Marshal.PtrToStructure(ptr, typeof(VSCOMPONENTSELECTORDATA));
                if (null == references.AddReferenceFromSelectorData(selectorData))
                {
                    //Skip further proccessing since a reference has to be added
                    pResult[0] = VSADDCOMPRESULT.ADDCOMPRESULT_Failure;
                    return VSConstants.S_OK;
                }
            }
            return VSConstants.S_OK;
        }

        #endregion

        #region IVsSccProject2 Members
        /// <summary>
        /// This method is called to determine which files should be placed under source control for a given VSITEMID within this hierarchy.
        /// </summary>
        /// <param name="itemid">Identifier for the VSITEMID being queried.</param>
        /// <param name="stringsOut">Pointer to an array of CALPOLESTR strings containing the file names for this item.</param>
        /// <param name="flagsOut">Pointer to a CADWORD array of flags stored in DWORDs indicating that some of the files have special behaviors.</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
        public virtual int GetSccFiles(uint itemid, CALPOLESTR[] stringsOut, CADWORD[] flagsOut)
        {
            if (itemid == VSConstants.VSITEMID_SELECTION)
            {
                throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid");
            }
            else if (itemid == VSConstants.VSITEMID_ROOT)
            {
                // Root node.  Return our project file path.
                if (stringsOut != null && stringsOut.Length > 0)
                {
                    stringsOut[0] = VsUtilities.CreateCALPOLESTR(new[] { filename });
                }

                if (flagsOut != null && flagsOut.Length > 0)
                {
                    flagsOut[0] = VsUtilities.CreateCADWORD(new[] { tagVsSccFilesFlags.SFF_NoFlags });
                }
                return VSConstants.S_OK;
            }

            // otherwise delegate to either a file or a folder to get the SCC files
            HierarchyNode n = this.NodeFromItemId(itemid);
            if (n == null)
            {
                throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid");
            }

            List<string> files = new List<string>();
            List<tagVsSccFilesFlags> flags = new List<tagVsSccFilesFlags>();

            n.GetSccFiles(files, flags);

            if (stringsOut != null && stringsOut.Length > 0)
            {
                stringsOut[0] = VsUtilities.CreateCALPOLESTR(files);
            }

            if (flagsOut != null && flagsOut.Length > 0)
            {
                flagsOut[0] = VsUtilities.CreateCADWORD(flags);
            }

            return VSConstants.S_OK;
        }

        public override void GetSccFiles(IList<string> files, IList<tagVsSccFilesFlags> flags)
        {
            for (HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling)
            {
                n.GetSccFiles(files, flags);
            }
        }

        public override void GetSccSpecialFiles(string sccFile, IList<string> files, IList<tagVsSccFilesFlags> flags)
        {
            for (HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling)
            {
                n.GetSccSpecialFiles(sccFile, files, flags);
            }
        }

        /// <summary>
        /// This method is called to discover special (hidden files) associated with a given VSITEMID within this hierarchy. 
        /// </summary>
        /// <param name="itemid">Identifier for the VSITEMID being queried.</param>
        /// <param name="sccFile">One of the files associated with the node</param>
        /// <param name="stringsOut">Pointer to an array of CALPOLESTR strings containing the file names for this item.</param>
        /// <param name="flagsOut">Pointer to a CADWORD array of flags stored in DWORDs indicating that some of the files have special behaviors.</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
        /// <remarks>This method is called to discover any special or hidden files associated with an item in the project hierarchy. It is called when GetSccFiles returns with the SFF_HasSpecialFiles flag set for any of the files associated with the node.</remarks>
        public virtual int GetSccSpecialFiles(uint itemid, string sccFile, CALPOLESTR[] stringsOut, CADWORD[] flagsOut)
        {
            if (itemid == VSConstants.VSITEMID_SELECTION)
            {
                throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid");
            }

            HierarchyNode n = this.NodeFromItemId(itemid);
            if (n == null)
            {
                throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid");
            }

            List<string> files = new List<string>();

            List<tagVsSccFilesFlags> flags = new List<tagVsSccFilesFlags>();

            n.GetSccSpecialFiles(sccFile, files, flags);

            if (stringsOut != null && stringsOut.Length > 0)
            {
                stringsOut[0] = VsUtilities.CreateCALPOLESTR(files);
            }

            if (flagsOut != null && flagsOut.Length > 0)
            {
                flagsOut[0] = VsUtilities.CreateCADWORD(flags);
            }

            // we have no special files.
            return VSConstants.S_OK;
        }

        /// <summary>
        /// This method is called by the source control portion of the environment to inform the project of changes to the source control glyph on various nodes. 
        /// </summary>
        /// <param name="affectedNodes">Count of changed nodes.</param>
        /// <param name="itemidAffectedNodes">An array of VSITEMID identifiers of the changed nodes.</param>
        /// <param name="newGlyphs">An array of VsStateIcon glyphs representing the new state of the corresponding item in rgitemidAffectedNodes.</param>
        /// <param name="newSccStatus">An array of status flags from SccStatus corresponding to rgitemidAffectedNodes. </param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
        public virtual int SccGlyphChanged(int affectedNodes, uint[] itemidAffectedNodes, VsStateIcon[] newGlyphs, uint[] newSccStatus)
        {
            // if all the paramaters are null adn the count is 0, it means scc wants us to updated everything
            if (affectedNodes == 0 && itemidAffectedNodes == null && newGlyphs == null && newSccStatus == null)
            {
                ReDrawNode(this, UIHierarchyElement.SccState);
                this.UpdateSccStateIcons();
            }
            else if (affectedNodes > 0 && itemidAffectedNodes != null && newGlyphs != null && newSccStatus != null)
            {
                for (int i = 0; i < affectedNodes; i++)
                {
                    HierarchyNode n = this.NodeFromItemId(itemidAffectedNodes[i]);
                    if (n == null)
                    {
                        throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemidAffectedNodes");
                    }

                    ReDrawNode(n, UIHierarchyElement.SccState);
                }
            }
            return VSConstants.S_OK;
        }

        /// <summary>
        /// This method is called by the source control portion of the environment when a project is initially added to source control, or to change some of the project's settings.
        /// </summary>
        /// <param name="sccProjectName">String, opaque to the project, that identifies the project location on the server. Persist this string in the project file. </param>
        /// <param name="sccLocalPath">String, opaque to the project, that identifies the path to the server. Persist this string in the project file.</param>
        /// <param name="sccAuxPath">String, opaque to the project, that identifies the local path to the project. Persist this string in the project file.</param>
        /// <param name="sccProvider">String, opaque to the project, that identifies the source control package. Persist this string in the project file.</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
        public virtual int SetSccLocation(string sccProjectName, string sccAuxPath, string sccLocalPath, string sccProvider)
        {
            VsUtilities.ArgumentNotNull("sccProjectName", sccProjectName);
            VsUtilities.ArgumentNotNull("sccAuxPath", sccAuxPath);
            VsUtilities.ArgumentNotNull("sccLocalPath", sccLocalPath);
            VsUtilities.ArgumentNotNull("sccProvider", sccProvider);

            // Save our settings (returns true if something changed)
            if (!this.SetSccSettings(sccProjectName, sccLocalPath, sccAuxPath, sccProvider))
            {
                return VSConstants.S_OK;
            }

            bool unbinding = (sccProjectName.Length == 0 && sccProvider.Length == 0);

            if (unbinding || this.QueryEditProjectFile(false))
            {
                this.buildProject.SetProperty(ProjectFileConstants.SccProjectName, sccProjectName);
                this.buildProject.SetProperty(ProjectFileConstants.SccProvider, sccProvider);
                this.buildProject.SetProperty(ProjectFileConstants.SccAuxPath, sccAuxPath);
                this.buildProject.SetProperty(ProjectFileConstants.SccLocalPath, sccLocalPath);
                SetProjectFileDirty(true);
            }

            this.isRegisteredWithScc = true;

            return VSConstants.S_OK;
        }
        #endregion

        #region IVsProjectSpecialFiles Members
        /// <summary>
        /// Allows you to query the project for special files and optionally create them. 
        /// </summary>
        /// <param name="fileId">__PSFFILEID of the file</param>
        /// <param name="flags">__PSFFLAGS flags for the file</param>
        /// <param name="itemid">The itemid of the node in the hierarchy</param>
        /// <param name="fileName">The file name of the special file.</param>
        /// <returns></returns>
        public virtual int GetFile(int fileId, uint flags, out uint itemid, out string fileName)
        {
            itemid = VSConstants.VSITEMID_NIL;
            fileName = String.Empty;

            // We need to return S_OK, otherwise the property page tabs will not be shown.
            return VSConstants.E_NOTIMPL;
        }
        #endregion

        #region IAggregatedHierarchy Members

        /// <summary>
        /// Get the inner object of an aggregated hierarchy
        /// </summary>
        /// <returns>A HierarchyNode</returns>
        public virtual HierarchyNode GetInner()
        {
            return this;
        }

        #endregion

        #region IReferenceDataProvider Members
        /// <summary>
        /// Returns the reference container node.
        /// </summary>
        /// <returns></returns>
        public IReferenceContainer GetReferenceContainer()
        {
            var container = FindImmediateChild(node => node is ReferenceContainerNode) as ReferenceContainerNode;
            if (container == null)
            {
                container = this.CreateReferenceContainerNode();
                if (container != null)
                    this.AddChild(container);
            }

            return container;
        }

#if DEV11_OR_LATER
        public IVsReferenceManagerUser GetReferenceManagerUser(IVsReferenceProviderContext[] contexts)
        {
            return new ReferenceManagerUser(contexts, this.GetReferenceContainer());
        }
#endif

        #endregion

        #region IBuildDependencyUpdate Members

        public virtual IVsBuildDependency[] BuildDependencies
        {
            get
            {
                return this.buildDependencyList.ToArray();
            }
        }

        public virtual void AddBuildDependency(IVsBuildDependency dependency)
        {
            if (this.isClosed || dependency == null)
            {
                return;
            }

            if (!this.buildDependencyList.Contains(dependency))
            {
                this.buildDependencyList.Add(dependency);
            }
        }

        public virtual void RemoveBuildDependency(IVsBuildDependency dependency)
        {
            if (this.isClosed || dependency == null)
            {
                return;
            }

            if (this.buildDependencyList.Contains(dependency))
            {
                this.buildDependencyList.Remove(dependency);
            }
        }

        #endregion

        #region IProjectEventsListener Members
        public bool IsProjectEventsListener
        {
            get { return this.isProjectEventsListener; }
            set { this.isProjectEventsListener = value; }
        }
        #endregion

        #region IVsAggregatableProject Members

        /// <summary>
        /// Retrieve the list of project GUIDs that are aggregated together to make this project.
        /// </summary>
        /// <param name="projectTypeGuids">Semi colon separated list of Guids. Typically, the last GUID would be the GUID of the base project factory</param>
        /// <returns>HResult</returns>
        public int GetAggregateProjectTypeGuids(out string projectTypeGuids)
        {
            projectTypeGuids = this.GetProjectProperty(ProjectFileConstants.ProjectTypeGuids);
            // In case someone manually removed this from our project file, default to our project without flavors
            if (String.IsNullOrEmpty(projectTypeGuids))
                projectTypeGuids = this.ProjectGuid.ToString("B");
            return VSConstants.S_OK;
        }

        /// <summary>
        /// This is where the initialization occurs.
        /// </summary>
        public virtual int InitializeForOuter(string filename, string location, string name, uint flags, ref Guid iid, out IntPtr projectPointer, out int canceled)
        {
            canceled = 0;
            projectPointer = IntPtr.Zero;

            // Initialize the project
            //Make this async, so we don't block the ui for the total time and other projects can load too.
             this.Load(filename, location, name, flags, ref iid, out canceled);

            if (canceled != 1)
            {
                // Set ourself as the project
                return Marshal.QueryInterface(Marshal.GetIUnknownForObject(this), ref iid, out projectPointer);
            }

            return VSConstants.OLE_E_PROMPTSAVECANCELLED;
        }

        /// <summary>
        /// This is called after the project is done initializing the different layer of the aggregations
        /// </summary>
        /// <returns>HResult</returns>
        public virtual int OnAggregationComplete()
        {
            return VSConstants.S_OK;
        }

        /// <summary>
        /// Set the list of GUIDs that are aggregated together to create this project.
        /// </summary>
        /// <param name="projectTypeGuids">Semi-colon separated list of GUIDs, the last one is usually the project factory of the base project factory</param>
        /// <returns>HResult</returns>
        public int SetAggregateProjectTypeGuids(string projectTypeGuids)
        {
            this.SetProjectProperty(ProjectFileConstants.ProjectTypeGuids, projectTypeGuids);
            return VSConstants.S_OK;
        }

        /// <summary>
        /// We are always the inner most part of the aggregation
        /// and as such we don't support setting an inner project
        /// </summary>
        public int SetInnerProject(object innerProject)
        {
            return VSConstants.E_NOTIMPL;
        }

        #endregion

        #region IVsProjectFlavorCfgProvider Members

        int IVsProjectFlavorCfgProvider.CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCfg ppFlavorCfg)
        {
            // Our config object is also our IVsProjectFlavorCfg object
            ppFlavorCfg = pBaseProjectCfg as IVsProjectFlavorCfg;

            return VSConstants.S_OK;
        }

        #endregion

        #region IVsBuildPropertyStorage Members

        /// <summary>
        /// Get the property of an item
        /// </summary>
        /// <param name="item">ItemID</param>
        /// <param name="attributeName">Name of the property</param>
        /// <param name="attributeValue">Value of the property (out parameter)</param>
        /// <returns>HRESULT</returns>
        int IVsBuildPropertyStorage.GetItemAttribute(uint item, string attributeName, out string attributeValue)
        {
            attributeValue = null;

            HierarchyNode node = NodeFromItemId(item);
            if (node == null)
                throw new ArgumentException("Invalid item id", "item");

            if (node.ItemNode != null) {
                attributeValue = node.ItemNode.GetMetadata(attributeName);
            } else if(node == node.ProjectMgr) {
                attributeName = node.ProjectMgr.GetProjectProperty(attributeName);
            }
            return VSConstants.S_OK;
        }

        /// <summary>
        /// Get the value of the property in the project file
        /// </summary>
        /// <param name="propertyName">Name of the property to remove</param>
        /// <param name="configName">Configuration for which to remove the property</param>
        /// <param name="storage">Project or user file (_PersistStorageType)</param>
        /// <param name="propertyValue">Value of the property (out parameter)</param>
        /// <returns>HRESULT</returns>
        int IVsBuildPropertyStorage.GetPropertyValue(string propertyName, string configName, uint storage, out string propertyValue)
        {
            // TODO: when adding support for User files, we need to update this method
            propertyValue = null;
            if (string.IsNullOrEmpty(configName))
            {
                propertyValue = this.GetProjectProperty(propertyName);
            }
            else
            {
                IVsCfg configurationInterface;
                int platformStart;
                if ((platformStart = configName.IndexOf('|')) != -1) {
                    // matches C# project system, GetPropertyValue handles display name, not just config name
                    configName = configName.Substring(0, platformStart);
                }
                ErrorHandler.ThrowOnFailure(this.ConfigProvider.GetCfgOfName(configName, string.Empty, out configurationInterface));
                Config config = (Config)configurationInterface;
                propertyValue = config.GetConfigurationProperty(propertyName, true);
            }
            return VSConstants.S_OK;
        }

        /// <summary>
        /// Delete a property
        /// In our case this simply mean defining it as null
        /// </summary>
        /// <param name="propertyName">Name of the property to remove</param>
        /// <param name="configName">Configuration for which to remove the property</param>
        /// <param name="storage">Project or user file (_PersistStorageType)</param>
        /// <returns>HRESULT</returns>
        int IVsBuildPropertyStorage.RemoveProperty(string propertyName, string configName, uint storage)
        {
            return ((IVsBuildPropertyStorage)this).SetPropertyValue(propertyName, configName, storage, null);
        }

        /// <summary>
        /// Set a property on an item
        /// </summary>
        /// <param name="item">ItemID</param>
        /// <param name="attributeName">Name of the property</param>
        /// <param name="attributeValue">New value for the property</param>
        /// <returns>HRESULT</returns>
        int IVsBuildPropertyStorage.SetItemAttribute(uint item, string attributeName, string attributeValue)
        {
            HierarchyNode node = NodeFromItemId(item);

            if (node == null)
                throw new ArgumentException("Invalid item id", "item");

            node.ItemNode.SetMetadata(attributeName, attributeValue);
            return VSConstants.S_OK;
        }

        /// <summary>
        /// Set a project property
        /// </summary>
        /// <param name="propertyName">Name of the property to set</param>
        /// <param name="configName">Configuration for which to set the property</param>
        /// <param name="storage">Project file or user file (_PersistStorageType)</param>
        /// <param name="propertyValue">New value for that property</param>
        /// <returns>HRESULT</returns>
        int IVsBuildPropertyStorage.SetPropertyValue(string propertyName, string configName, uint storage, string propertyValue)
        {
            // TODO: when adding support for User files, we need to update this method
            if (string.IsNullOrEmpty(configName))
            {
                this.SetProjectProperty(propertyName, propertyValue);
            }
            else
            {
                IVsCfg configurationInterface;
                ErrorHandler.ThrowOnFailure(this.ConfigProvider.GetCfgOfName(configName, string.Empty, out configurationInterface));
                Config config = (Config)configurationInterface;
                config.SetConfigurationProperty(propertyName, propertyValue);
            }
            return VSConstants.S_OK;
        }

        #endregion

        #region private helper methods

        /// <summary>
        /// Initialize projectNode
        /// </summary>
        private void Initialize()
        {
            this.ID = VSConstants.VSITEMID_ROOT;
            this.tracker = new TrackDocumentsHelper(this);

            uint cookie;
            this.AdviseHierarchyEvents(new HierarchyEventsSink(this), out cookie);

            var serviceProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)(((IServiceProvider)this.Package).GetService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)));
            this.SetSite(serviceProvider);
        }

        /// <summary>
        /// Add an item to the hierarchy based on the item path
        /// </summary>
        /// <param name="item">Item to add</param>
        /// <returns>Added node</returns>
        private HierarchyNode AddIndependentFileNode(MSBuild.ProjectItem item, HierarchyNode parent)
        {
            return AddFileNodeToNode(item, parent);
        }

        /// <summary>
        /// Add a dependent file node to the hierarchy
        /// </summary>
        /// <param name="item">msbuild item to add</param>
        /// <param name="parentNode">Parent Node</param>
        /// <returns>Added node</returns>
        private HierarchyNode AddDependentFileNodeToNode(MSBuild.ProjectItem item, HierarchyNode parentNode)
        {
            FileNode node = this.CreateDependentFileNode(new MsBuildProjectElement(this, item));
            parentNode.AddChild(node);

            // Make sure to set the HasNameRelation flag on the dependent node if it is related to the parent by name
            if (!node.HasParentNodeNameRelation && string.Compare(node.GetRelationalName(), parentNode.GetRelationalName(), StringComparison.OrdinalIgnoreCase) == 0)
            {
                node.HasParentNodeNameRelation = true;
            }

            return node;
        }

        /// <summary>
        /// Add a file node to the hierarchy
        /// </summary>
        /// <param name="item">msbuild item to add</param>
        /// <param name="parentNode">Parent Node</param>
        /// <returns>Added node</returns>
        private HierarchyNode AddFileNodeToNode(MSBuild.ProjectItem item, HierarchyNode parentNode)
        {
            FileNode node = this.CreateFileNode(new MsBuildProjectElement(this, item));
            parentNode.AddChild(node);
            return node;
        }

        /// <summary>
        /// Get the parent node of an msbuild item
        /// </summary>
        /// <param name="item">msbuild item</param>
        /// <returns>parent node</returns>
        private HierarchyNode GetItemParentNode(MSBuild.ProjectItem item)
        {
            var link = item.GetMetadataValue(ProjectFileConstants.Link);
            HierarchyNode currentParent = this;
            string strPath = item.EvaluatedInclude;

            if (!String.IsNullOrWhiteSpace(link))
            {
                strPath = Path.GetDirectoryName(link);
            }
            else
            {
                string absPath = CommonUtils.GetAbsoluteFilePath(ProjectHome, strPath);

                if (CommonUtils.IsSubpathOf(ProjectHome, absPath))
                {
                    strPath = CommonUtils.GetRelativeDirectoryPath(ProjectHome, Path.GetDirectoryName(absPath));
                }
                else
                {
                    // file lives outside of the project, w/o a link it's just at the top level.
                    return this;
                }
            }

            if (strPath.Length > 0)
            {
                // Use the relative to verify the folders...
                currentParent = this.CreateFolderNodes(strPath);
            }
            return currentParent;
        }

        private MSBuildExecution.ProjectPropertyInstance GetMsBuildProperty(string propertyName, bool resetCache)
        {
            if (resetCache || this.currentConfig == null)
            {
                // Get properties from project file and cache it
                this.SetCurrentConfiguration();
                this.currentConfig = this.buildProject.CreateProjectInstance();
            }

            if (this.currentConfig == null)
                throw new Exception(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FailedToRetrieveProperties, CultureInfo.CurrentUICulture), propertyName));

            // return property asked for
            return this.currentConfig.GetProperty(propertyName);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
        private string GetAssemblyName(MSBuildExecution.ProjectInstance properties)
        {
            this.currentConfig = properties;
            string name = null;

            name = GetProjectProperty(ProjectFileConstants.AssemblyName);
            if (name == null)
                name = this.Caption;

            string outputtype = GetProjectProperty(ProjectFileConstants.OutputType, false);

            if (outputtype == "library")
            {
                outputtype = outputtype.ToLowerInvariant();
                name += ".dll";
            }
            else
            {
                name += ".exe";
            }

            return name;
        }

        /// <summary>
        /// Updates our scc project settings. 
        /// </summary>
        /// <param name="sccProjectName">String, opaque to the project, that identifies the project location on the server. Persist this string in the project file. </param>
        /// <param name="sccLocalPath">String, opaque to the project, that identifies the path to the server. Persist this string in the project file.</param>
        /// <param name="sccAuxPath">String, opaque to the project, that identifies the local path to the project. Persist this string in the project file.</param>
        /// <param name="sccProvider">String, opaque to the project, that identifies the source control package. Persist this string in the project file.</param>
        /// <returns>Returns true if something changed.</returns>
        private bool SetSccSettings(string sccProjectName, string sccLocalPath, string sccAuxPath, string sccProvider)
        {
            bool changed = false;
            Debug.Assert(sccProjectName != null && sccLocalPath != null && sccAuxPath != null && sccProvider != null);
            if (String.Compare(sccProjectName, this.sccProjectName, StringComparison.OrdinalIgnoreCase) != 0 ||
                String.Compare(sccLocalPath, this.sccLocalPath, StringComparison.OrdinalIgnoreCase) != 0 ||
                String.Compare(sccAuxPath, this.sccAuxPath, StringComparison.OrdinalIgnoreCase) != 0 ||
                String.Compare(sccProvider, this.sccProvider, StringComparison.OrdinalIgnoreCase) != 0)
            {
                changed = true;
                this.sccProjectName = sccProjectName;
                this.sccLocalPath = sccLocalPath;
                this.sccAuxPath = sccAuxPath;
                this.sccProvider = sccProvider;
            }


            return changed;
        }

        /// <summary>
        /// Sets the scc info from the project file.
        /// </summary>
        protected void InitSccInfo()
        {
            this.sccProjectName = this.GetProjectProperty(ProjectFileConstants.SccProjectName);
            this.sccLocalPath = this.GetProjectProperty(ProjectFileConstants.SccLocalPath);
            this.sccProvider = this.GetProjectProperty(ProjectFileConstants.SccProvider);
            this.sccAuxPath = this.GetProjectProperty(ProjectFileConstants.SccAuxPath);
        }

        private void OnAfterProjectOpen(object sender, AfterProjectFileOpenedEventArgs e)
        {
            this.projectOpened = true;
        }

        private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor, string configuration, string fragment)
        {
            XmlElement node = document.CreateElement(ProjectFileConstants.FlavorProperties);
            XmlAttribute attribute = document.CreateAttribute(ProjectFileConstants.Guid);
            attribute.Value = flavor.ToString("B");
            node.Attributes.Append(attribute);
            if (!String.IsNullOrEmpty(configuration))
            {
                attribute = document.CreateAttribute(ProjectFileConstants.Configuration);
                attribute.Value = configuration;
                node.Attributes.Append(attribute);
            }
            node.InnerXml = fragment;
            root.AppendChild(node);
            return node;
        }

        /// <summary>
        /// Sets the project guid from the project file. If no guid is found a new one is created and assigne for the instance project guid.
        /// </summary>
        private void SetProjectGuidFromProjectFile()
        {
            string projectGuid = this.GetProjectProperty(ProjectFileConstants.ProjectGuid);
            if (String.IsNullOrEmpty(projectGuid))
            {
                this.projectIdGuid = Guid.NewGuid();
            }
            else
            {
                Guid guid = new Guid(projectGuid);
                if (guid != this.projectIdGuid)
                {
                    this.projectIdGuid = guid;
                }
            }
        }

        /// <summary>
        /// Helper for sharing common code between Build() and BuildAsync()
        /// </summary>
        /// <param name="output"></param>
        /// <returns></returns>
        private bool BuildPrelude(IVsOutputWindowPane output)
        {
            bool engineLogOnlyCritical = false;
            // If there is some output, then we can ask the build engine to log more than
            // just the critical events.
            if (null != output)
            {
                engineLogOnlyCritical = BuildEngine.OnlyLogCriticalEvents;
                BuildEngine.OnlyLogCriticalEvents = false;
            }

            this.SetOutputLogger(output);
            return engineLogOnlyCritical;
        }

        /// <summary>
        /// Recusively parses the tree and closes all nodes.
        /// </summary>
        /// <param name="node">The subtree to close.</param>
        private static void CloseAllNodes(HierarchyNode node)
        {
            for (HierarchyNode n = node.FirstChild; n != null; n = n.NextSibling)
            {
                if (n.FirstChild != null)
                {
                    CloseAllNodes(n);
                }

                n.Close();
            }
        }

        /// <summary>
        /// Set the build project with the new project instance value
        /// </summary>
        /// <param name="project">The new build project instance</param>
        private void SetBuildProject(MSBuild.Project project)
        {
            bool isNewBuildProject = (this.buildProject != project);
            this.buildProject = project;
            if (this.buildProject != null)
            {
                SetupProjectGlobalPropertiesThatAllProjectSystemsMustSet();
            }
            if (isNewBuildProject)
            {
                NewBuildProject(project);
            }
        }

        /// <summary>
        /// Called when a new value for <see cref="BuildProject"/> is available.
        /// </summary>
        protected virtual void NewBuildProject(MSBuild.Project project) { }

        /// <summary>
        /// Setup the global properties for project instance.
        /// </summary>
        private void SetupProjectGlobalPropertiesThatAllProjectSystemsMustSet()
        {
            string solutionDirectory = null;
            string solutionFile = null;
            string userOptionsFile = null;

            IVsSolution solution = this.Site.GetService(typeof(SVsSolution)) as IVsSolution;
            if (solution != null)
            {
                // We do not want to throw. If we cannot set the solution related constants we set them to empty string.
                solution.GetSolutionInfo(out solutionDirectory, out solutionFile, out userOptionsFile);
            }

            if (solutionDirectory == null)
            {
                solutionDirectory = String.Empty;
            }

            if (solutionFile == null)
            {
                solutionFile = String.Empty;
            }

            string solutionFileName = Path.GetFileName(solutionFile);
            string solutionName = Path.GetFileNameWithoutExtension(solutionFile);
            string solutionExtension = Path.GetExtension(solutionFile);

            this.buildProject.SetGlobalProperty(GlobalProperty.SolutionDir.ToString(), solutionDirectory);
            this.buildProject.SetGlobalProperty(GlobalProperty.SolutionPath.ToString(), solutionFile);
            this.buildProject.SetGlobalProperty(GlobalProperty.SolutionFileName.ToString(), solutionFileName);
            this.buildProject.SetGlobalProperty(GlobalProperty.SolutionName.ToString(), solutionName);
            this.buildProject.SetGlobalProperty(GlobalProperty.SolutionExt.ToString(), solutionExtension);

            // Other misc properties
            this.buildProject.SetGlobalProperty(GlobalProperty.BuildingInsideVisualStudio.ToString(), "true");
            this.buildProject.SetGlobalProperty(GlobalProperty.Configuration.ToString(), Config.Debug);
            this.buildProject.SetGlobalProperty(GlobalProperty.Platform.ToString(), Config.AnyCPU);

            // DevEnvDir property
            object installDirAsObject = null;

            IVsShell shell = this.Site.GetService(typeof(SVsShell)) as IVsShell;
            if (shell != null)
            {
                // We do not want to throw. If we cannot set the solution related constants we set them to empty string.
                shell.GetProperty((int)__VSSPROPID.VSSPROPID_InstallDirectory, out installDirAsObject);
            }

            // Ensure that we have traimnling backslash as this is done for the langproj macros too.
            string installDir = CommonUtils.NormalizeDirectoryPath((string)installDirAsObject) ?? String.Empty;

            this.buildProject.SetGlobalProperty(GlobalProperty.DevEnvDir.ToString(), installDir);
        }


        /// <summary>
        /// Attempts to lock in the privilege of running a build in Visual Studio.
        /// </summary>
        /// <param name="designTime"><c>false</c> if this build was called for by the Solution Build Manager; <c>true</c> otherwise.</param>
        /// <param name="requiresUIThread">
        /// Need to claim the UI thread for build under the following conditions:
        /// 1. The build must use a resource that uses the UI thread, such as
        /// - you set HostServices and you have a host object which requires (even indirectly) the UI thread (VB and C# compilers do this for instance.)
        /// or,
        /// 2. The build requires the in-proc node AND waits on the UI thread for the build to complete, such as:
        /// - you use a ProjectInstance to build, or
        /// - you have specified a host object, whether or not it requires the UI thread, or
        /// - you set HostServices and you have specified a node affinity.
        /// - In addition to the above you also call submission.Execute(), or you call submission.ExecuteAsync() and then also submission.WaitHandle.Wait*().
        /// </param>
        /// <returns>A value indicating whether a build may proceed.</returns>
        /// <remarks>
        /// This method must be called on the UI thread.
        /// </remarks>
        protected bool TryBeginBuild(bool designTime, bool requiresUIThread = false)
        {
            IVsBuildManagerAccessor accessor = null;

            if (this.Site != null)
            {
                accessor = this.Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor;
            }

            bool releaseUIThread = false;

            try
            {
                // If the SVsBuildManagerAccessor service is absent, we're not running within Visual Studio.
                if (accessor != null)
                {
                    if (requiresUIThread)
                    {
                        int result = accessor.ClaimUIThreadForBuild();
                        if (result < 0)
                        {
                            // Not allowed to claim the UI thread right now. Try again later.
                            return false;
                        }

                        releaseUIThread = true; // assume we need to release this immediately until we get through the whole gauntlet.
                    }

                    if (designTime)
                    {
                        int result = accessor.BeginDesignTimeBuild();
                        if (result < 0)
                        {
                            // Not allowed to begin a design-time build at this time. Try again later.
                            return false;
                        }
                    }

                    // We obtained all the resources we need.  So don't release the UI thread until after the build is finished.
                    releaseUIThread = false;
                }
                else
                {
                    BuildParameters buildParameters = new BuildParameters(this.buildEngine);
                    BuildManager.DefaultBuildManager.BeginBuild(buildParameters);
                }

                this.buildInProcess = true;
                return true;
            }
            finally
            {
                // If we were denied the privilege of starting a design-time build,
                // we need to release the UI thread.
                if (releaseUIThread)
                {
                    Debug.Assert(accessor != null, "We think we need to release the UI thread for an accessor we don't have!");
                    accessor.ReleaseUIThreadForBuild();
                }
            }
        }

        /// <summary>
        /// Lets Visual Studio know that we're done with our design-time build so others can use the build manager.
        /// </summary>
        /// <param name="submission">The build submission that built, if any.</param>
        /// <param name="designTime">This must be the same value as the one passed to <see cref="TryBeginBuild"/>.</param>
        /// <param name="requiresUIThread">This must be the same value as the one passed to <see cref="TryBeginBuild"/>.</param>
        /// <remarks>
        /// This method must be called on the UI thread.
        /// </remarks>
        protected virtual void EndBuild(uint vsopts, BuildSubmission submission, bool designTime, bool requiresUIThread = false)
        {
            IVsBuildManagerAccessor accessor = null;

            if (this.Site != null)
            {
                try
                {
                    accessor = this.Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor;
                }
                catch { }
            }

            if (accessor != null)
            {
                // It's very important that we try executing all three end-build steps, even if errors occur partway through.
                try
                {
                    if (submission != null)
                    {
                        Marshal.ThrowExceptionForHR(accessor.UnregisterLoggers(submission.SubmissionId));
                    }
                }
                catch (Exception ex)
                {
                    if (ErrorHandler.IsCriticalException(ex))
                    {
                        throw;
                    }

                    Trace.TraceError(ex.ToString());
                }

                try
                {
                    if (designTime)
                    {
                        Marshal.ThrowExceptionForHR(accessor.EndDesignTimeBuild());
                    }
                }
                catch (Exception ex)
                {
                    if (ErrorHandler.IsCriticalException(ex))
                    {
                        throw;
                    }

                    Trace.TraceError(ex.ToString());
                }


                try
                {
                    if (requiresUIThread)
                    {
                        Marshal.ThrowExceptionForHR(accessor.ReleaseUIThreadForBuild());
                    }
                }
                catch (Exception ex)
                {
                    if (ErrorHandler.IsCriticalException(ex))
                    {
                        throw;
                    }

                    Trace.TraceError(ex.ToString());
                }
            }
            else
            {
                BuildManager.DefaultBuildManager.EndBuild();
            }

            this.buildInProcess = false;
        }

        #endregion

        #region IProjectEventsCallback Members

        public virtual void BeforeClose()
        {
        }

        #endregion

        #region IVsProjectBuildSystem Members

        public virtual int SetHostObject(string targetName, string taskName, object hostObject) {
            Debug.Assert(targetName != null && taskName != null && this.buildProject != null && this.buildProject.Targets != null);

            if (targetName == null || taskName == null || this.buildProject == null || this.buildProject.Targets == null) {
                return VSConstants.E_INVALIDARG;
            }

            this.buildProject.ProjectCollection.HostServices.RegisterHostObject(this.buildProject.FullPath, targetName, taskName, (Microsoft.Build.Framework.ITaskHost)hostObject);

            return VSConstants.S_OK;
        }

        public int BuildTarget(string targetName, out bool success) {
            success = false;

            MSBuildResult result = this.Build(targetName);

            if (result == MSBuildResult.Successful) {
                success = true;
            }

            return VSConstants.S_OK;
        }

        public virtual int CancelBatchEdit() {
            return VSConstants.E_NOTIMPL;
        }

        public virtual int EndBatchEdit() {
            return VSConstants.E_NOTIMPL;
        }

        public virtual int StartBatchEdit() {
            return VSConstants.E_NOTIMPL;
        }

        /// <summary>
        /// Used to determine the kind of build system, in VS 2005 there's only one defined kind: MSBuild 
        /// </summary>
        /// <param name="kind"></param>
        /// <returns></returns>
        public virtual int GetBuildSystemKind(out uint kind) {
            kind = (uint)_BuildSystemKindFlags2.BSK_MSBUILD_VS10;
            return VSConstants.S_OK;
        }

        #endregion

        /// <summary>
        /// Finds a node by it's full path on disk.
        /// </summary>
        public HierarchyNode FindNodeByFullPath(string name) {
            Debug.Assert(Path.IsPathRooted(name));
            HierarchyNode res;
            _diskNodes.TryGetValue(name, out res);
            return res;
        }

        #region IVsUIHierarchy methods

        public virtual int ExecCommand(uint itemId, ref Guid guidCmdGroup, uint nCmdId, uint nCmdExecOpt, IntPtr pvain, IntPtr p) {
            return this.publicExecCommand(guidCmdGroup, nCmdId, nCmdExecOpt, pvain, p, CommandOrigin.UiHierarchy);
        }

        public virtual int QueryStatusCommand(uint itemId, ref Guid guidCmdGroup, uint cCmds, OLECMD[] cmds, IntPtr pCmdText) {
            return this.QueryStatusSelection(guidCmdGroup, cCmds, cmds, pCmdText, CommandOrigin.UiHierarchy);
        }

        int IVsUIHierarchy.Close() {
            return ((IVsHierarchy)this).Close();
        }

        #endregion

        #region IVsHierarchy methods

        public virtual int AdviseHierarchyEvents(IVsHierarchyEvents sink, out uint cookie) {
            cookie = this._hierarchyEventSinks.Add(sink) + 1;
            return VSConstants.S_OK;
        }

        /// <summary>
        /// Closes the project node.
        /// </summary>
        /// <returns>A success or failure value.</returns>
        int IVsHierarchy.Close() {
            int hr = VSConstants.S_OK;
            try {
                // Walk the tree and close all nodes.
                // This has to be done before the project closes, since we want still state available for the ProjectMgr on the nodes 
                // when nodes are closing.
                CloseAllNodes(this);
            } catch (COMException e) {
                hr = e.ErrorCode;
            } finally {
                Close();
            }

            this.isClosed = true;

            return hr;
        }

        /// <summary>
        /// Sets the service provider from which to access the services. 
        /// </summary>
        /// <param name="site">An instance to an Microsoft.VisualStudio.OLE.Interop object</param>
        /// <returns>A success or failure value.</returns>
        public virtual int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider site) {
            this.site = new ServiceProvider(site);

            if (taskProvider != null) {
                taskProvider.Dispose();
            }
            taskProvider = new TaskProvider(this.site);

            return VSConstants.S_OK;
        }

        public virtual int GetCanonicalName(uint itemId, out string name) {
            HierarchyNode n = NodeFromItemId(itemId);
            name = (n != null) ? n.GetCanonicalName() : null;
            return VSConstants.S_OK;
        }

        public virtual int GetGuidProperty(uint itemId, int propid, out Guid guid) {
            guid = Guid.Empty;
            HierarchyNode n = NodeFromItemId(itemId);
            if (n != null) {
                int hr = n.GetGuidProperty(propid, out guid);
                __VSHPROPID vspropId = (__VSHPROPID)propid;
                return hr;
            }
            if (guid == Guid.Empty) {
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }
            return VSConstants.S_OK;
        }


        public virtual int GetProperty(uint itemId, int propId, out object propVal) {
            propVal = null;
            if (itemId != VSConstants.VSITEMID_ROOT && propId == (int)__VSHPROPID.VSHPROPID_IconImgList) {
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }

            HierarchyNode n = NodeFromItemId(itemId);
            if (n != null) {
                propVal = n.GetProperty(propId);
            }
            if (propVal == null) {
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }
            return VSConstants.S_OK;
        }

        public virtual int GetNestedHierarchy(uint itemId, ref Guid iidHierarchyNested, out IntPtr ppHierarchyNested, out uint pItemId) {
            ppHierarchyNested = IntPtr.Zero;
            pItemId = 0;
            // If itemid is not a nested hierarchy we must return E_FAIL.
            return VSConstants.E_FAIL;
        }

        public virtual int GetSite(out Microsoft.VisualStudio.OLE.Interop.IServiceProvider site) {
            site = Site.GetService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)) as Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
            return VSConstants.S_OK;
        }

        /// <summary>
        /// the canonicalName of an item is it's URL, or better phrased,
        /// the persistence data we put into @RelPath, which is a relative URL
        /// to the root project
        /// returning the itemID from this means scanning the list
        /// </summary>
        /// <param name="name"></param>
        /// <param name="itemId"></param>
        public virtual int ParseCanonicalName(string name, out uint itemId) {
            // we always start at the current node and go it's children down, so 
            //  if you want to scan the whole tree, better call 
            // the root
            name = EnsureRootedPath(name);
            itemId = 0;
            var child = FindNodeByFullPath(name);
            if (child != null) {
                itemId = child.HierarchyId;
                return VSConstants.S_OK;
            }

            return VSConstants.E_FAIL;
        }

        private string EnsureRootedPath(string name) {
            if (!Path.IsPathRooted(name)) {
                name = CommonUtils.GetAbsoluteDirectoryPath(
                    ProjectHome,
                    name
                );
            }
            return name;
        }

        public virtual int QueryClose(out int fCanClose) {
            fCanClose = 1;
            return VSConstants.S_OK;
        }

        public virtual int SetGuidProperty(uint itemId, int propid, ref Guid guid) {
            HierarchyNode n = NodeFromItemId(itemId);
            int rc = VSConstants.E_INVALIDARG;
            if (n != null) {
                rc = n.SetGuidProperty(propid, ref guid);
            }
            return rc;
        }

        public virtual int SetProperty(uint itemId, int propid, object value) {
            HierarchyNode n = NodeFromItemId(itemId);
            if (n != null) {
                return n.SetProperty(propid, value);
            } else {
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }
        }
        
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2233:OperationsShouldNotOverflow", MessageId = "cookie-1")]
        public virtual int UnadviseHierarchyEvents(uint cookie) {
            this._hierarchyEventSinks.RemoveAt(cookie - 1);
            return VSConstants.S_OK;
        }

        public int Unused0() {
            return VSConstants.E_NOTIMPL;
        }

        public int Unused1() {
            return VSConstants.E_NOTIMPL;
        }

        public int Unused2() {
            return VSConstants.E_NOTIMPL;
        }

        public int Unused3() {
            return VSConstants.E_NOTIMPL;
        }

        public int Unused4() {
            return VSConstants.E_NOTIMPL;
        }

        #endregion

        #region Hierarchy change notification

        public void OnItemAdded(HierarchyNode parent, HierarchyNode child) {
            VsUtilities.ArgumentNotNull("parent", parent);
            VsUtilities.ArgumentNotNull("child", child);

            IDiskBasedNode diskNode = child as IDiskBasedNode;
            if (diskNode != null) {
                _diskNodes[diskNode.Url] = child;
            }

            if ((EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) {
                return;
            }

            HierarchyNode prev = child.PreviousVisibleSibling;
            uint prevId = (prev != null) ? prev.HierarchyId : VSConstants.VSITEMID_NIL;
            foreach (IVsHierarchyEvents sink in _hierarchyEventSinks) {
                int result = sink.OnItemAdded(parent.HierarchyId, prevId, child.HierarchyId);
                if (ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) {
                    ErrorHandler.ThrowOnFailure(result);
                }
            }
        }

        public void OnItemDeleted(HierarchyNode deletedItem) {
            IDiskBasedNode diskNode = deletedItem as IDiskBasedNode;
            if (diskNode != null) {
                _diskNodes.Remove(diskNode.Url);
            }

            if ((EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) {
                return;
            }

            if (_hierarchyEventSinks.Count > 0) {
                // Note that in some cases (deletion of project node for example), an Advise
                // may be removed while we are iterating over it. To get around this problem we
                // take a snapshot of the advise list and walk that.
                List<IVsHierarchyEvents> clonedSink = new List<IVsHierarchyEvents>();

                foreach (IVsHierarchyEvents anEvent in _hierarchyEventSinks) {
                    clonedSink.Add(anEvent);
                }

                foreach (IVsHierarchyEvents clonedEvent in clonedSink) {
                    int result = clonedEvent.OnItemDeleted(deletedItem.HierarchyId);
                    if (ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) {
                        ErrorHandler.ThrowOnFailure(result);
                    }
                }
            }
        }

        public void OnItemsAppended(HierarchyNode parent) {
            VsUtilities.ArgumentNotNull("parent", parent);

            if ((EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) {
                return;
            }

            foreach (IVsHierarchyEvents sink in _hierarchyEventSinks) {
                int result = sink.OnItemsAppended(parent.HierarchyId);

                if (ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) {
                    ErrorHandler.ThrowOnFailure(result);
                }
            }
        }

        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")]
        public void OnPropertyChanged(HierarchyNode node, int propid, uint flags) {
            VsUtilities.ArgumentNotNull("node", node);

            if ((EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) {
                return;
            }

            foreach (IVsHierarchyEvents sink in _hierarchyEventSinks) {
                int result = sink.OnPropertyChanged(node.HierarchyId, propid, flags);

                if (ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) {
                    ErrorHandler.ThrowOnFailure(result);
                }
            }
        }

        /// <summary>
        /// Causes the hierarchy to be redrawn.
        /// </summary>
        /// <param name="element">Used by the hierarchy to decide which element to redraw</param>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Re")]
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ReDraw")]
        public void ReDrawNode(HierarchyNode node, UIHierarchyElement element) {
            foreach (IVsHierarchyEvents sink in _hierarchyEventSinks) {
                int result;
                if ((element & UIHierarchyElement.Icon) != 0) {
                    result = sink.OnPropertyChanged(node.ID, (int)__VSHPROPID.VSHPROPID_IconIndex, 0);
                    Debug.Assert(ErrorHandler.Succeeded(result), "Redraw failed for node " + this.GetMkDocument());
                }

                if ((element & UIHierarchyElement.Caption) != 0) {
                    result = sink.OnPropertyChanged(node.ID, (int)__VSHPROPID.VSHPROPID_Caption, 0);
                    Debug.Assert(ErrorHandler.Succeeded(result), "Redraw failed for node " + this.GetMkDocument());
                }

                if ((element & UIHierarchyElement.SccState) != 0) {
                    result = sink.OnPropertyChanged(node.ID, (int)__VSHPROPID.VSHPROPID_StateIconIndex, 0);
                    Debug.Assert(ErrorHandler.Succeeded(result), "Redraw failed for node " + this.GetMkDocument());
                }
            }
        }

        #endregion

        #region IVsHierarchyDeleteHandler methods

        public virtual int DeleteItem(uint delItemOp, uint itemId) {
            if (itemId == VSConstants.VSITEMID_SELECTION) {
                return VSConstants.E_INVALIDARG;
            }

            HierarchyNode node = NodeFromItemId(itemId);
            if (node != null) {
                node.Remove((delItemOp & (uint)__VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage) != 0);
                return VSConstants.S_OK;
            }

            return VSConstants.E_FAIL;
        }

        public virtual int QueryDeleteItem(uint delItemOp, uint itemId, out int candelete) {
            candelete = 0;
            if (itemId == VSConstants.VSITEMID_SELECTION) {
                return VSConstants.E_INVALIDARG;
            }

            // We ask the project what state it is. If he is a state that should not allow delete then we return.
            if (IsCurrentStateASuppressCommandsMode()) {
                return VSConstants.S_OK;
            }

            HierarchyNode node = NodeFromItemId(itemId);

            if (node == null) {
                return VSConstants.E_FAIL;
            }

            // Ask the nodes if they can remove the item.
            bool canDeleteItem = node.CanDeleteItem((__VSDELETEITEMOPERATION)delItemOp);
            if (canDeleteItem) {
                candelete = 1;
            }

            return VSConstants.S_OK;
        }

        #endregion

        #region IVsPersistHierarchyItem2 methods

        /// <summary>
        /// Saves the hierarchy item to disk. 
        /// </summary>
        /// <param name="saveFlag">Flags whose values are taken from the VSSAVEFLAGS enumeration.</param>
        /// <param name="silentSaveAsName">New filename when doing silent save as</param>
        /// <param name="itemid">Item identifier of the hierarchy item saved from VSITEMID.</param>
        /// <param name="docData">Item identifier of the hierarchy item saved from VSITEMID.</param>
        /// <param name="cancelled">[out] true if the save action was canceled.</param>
        /// <returns>[out] true if the save action was canceled.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        public virtual int SaveItem(VSSAVEFLAGS saveFlag, string silentSaveAsName, uint itemid, IntPtr docData, out int cancelled) {
            cancelled = 0;

            // Validate itemid 
            if (itemid == VSConstants.VSITEMID_ROOT || itemid == VSConstants.VSITEMID_SELECTION) {
                return VSConstants.E_INVALIDARG;
            }

            HierarchyNode node = this.NodeFromItemId(itemid);
            if (node == null) {
                return VSConstants.E_FAIL;
            }

            string existingFileMoniker = node.GetMkDocument();

            // We can only perform save if the document is open
            if (docData == IntPtr.Zero) {
                string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.CanNotSaveFileNotOpeneInEditor, CultureInfo.CurrentUICulture), node.Url);
                throw new InvalidOperationException(errorMessage);
            }

            string docNew = String.Empty;
            int returnCode = VSConstants.S_OK;
            IPersistFileFormat ff = null;
            IVsPersistDocData dd = null;
            IVsUIShell shell = Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
            VsUtilities.CheckNotNull(shell);

            try {
                //Save docdata object. 
                //For the saveas action a dialog is show in order to enter new location of file.
                //In case of a save action and the file is readonly a dialog is also shown
                //with a couple of options, SaveAs, Overwrite or Cancel.
                ff = Marshal.GetObjectForIUnknown(docData) as IPersistFileFormat;
                VsUtilities.CheckNotNull(ff);

                if (VSSAVEFLAGS.VSSAVE_SilentSave == saveFlag) {
                    ErrorHandler.ThrowOnFailure(shell.SaveDocDataToFile(saveFlag, ff, silentSaveAsName, out docNew, out cancelled));
                } else {
                    dd = Marshal.GetObjectForIUnknown(docData) as IVsPersistDocData;
                    VsUtilities.CheckNotNull(dd);

                    ErrorHandler.ThrowOnFailure(dd.SaveDocData(saveFlag, out docNew, out cancelled));
                }

                // We can be unloaded after the SaveDocData() call if the save caused a designer to add a file and this caused
                // the project file to be reloaded (QEQS caused a newer version of the project file to be downloaded). So we check
                // here.
                if (IsClosed) {
                    cancelled = 1;
                    return (int)OleConstants.OLECMDERR_E_CANCELED;
                } else {
                    // if a SaveAs occurred we need to update to the fact our item's name has changed.
                    // this includes the following:
                    // 	  1. call RenameDocument on the RunningDocumentTable
                    // 	  2. update the full path name for the item in our hierarchy
                    // 	  3. a directory-based project may need to transfer the open editor to the
                    // 	 	 MiscFiles project if the new file is saved outside of the project directory.
                    // 	 	 This is accomplished by calling IVsExternalFilesManager::TransferDocument                    

                    // we have three options for a saveas action to be performed
                    // 1. the flag was set (the save as command was triggered)
                    // 2. a silent save specifying a new document name
                    // 3. a save command was triggered but was not possible because the file has a read only attrib. Therefore
                    //    the user has chosen to do a save as in the dialog that showed up
                    bool emptyOrSamePath = String.IsNullOrEmpty(docNew) || CommonUtils.IsSamePath(existingFileMoniker, docNew);
                    bool saveAs = ((saveFlag == VSSAVEFLAGS.VSSAVE_SaveAs)) ||
                        ((saveFlag == VSSAVEFLAGS.VSSAVE_SilentSave) && !emptyOrSamePath) ||
                        ((saveFlag == VSSAVEFLAGS.VSSAVE_Save) && !emptyOrSamePath);

                    if (saveAs) {
                        returnCode = node.AfterSaveItemAs(docData, docNew);

                        // If it has been cancelled recover the old name.
                        if ((returnCode == (int)OleConstants.OLECMDERR_E_CANCELED || returnCode == VSConstants.E_ABORT)) {
                            // Cleanup.
                            this.DeleteFromStorage(docNew);

                            if (ff != null) {
                                returnCode = shell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, ff, existingFileMoniker, out docNew, out cancelled);
                            }
                        } else if (returnCode != VSConstants.S_OK) {
                            ErrorHandler.ThrowOnFailure(returnCode);
                        }
                    }
                }
            } catch (COMException e) {
                Trace.WriteLine("Exception :" + e.Message);
                returnCode = e.ErrorCode;

                // Try to recover
                // changed from MPFProj:
                // http://mpfproj10.codeplex.com/WorkItem/View.aspx?WorkItemId=6982
                if (ff != null && cancelled == 0) {
                    ErrorHandler.ThrowOnFailure(shell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, ff, existingFileMoniker, out docNew, out cancelled));
                }
            }

            return returnCode;
        }

        /// <summary>
        /// Determines whether the hierarchy item changed. 
        /// </summary>
        /// <param name="itemId">Item identifier of the hierarchy item contained in VSITEMID.</param>
        /// <param name="docData">Pointer to the IUnknown interface of the hierarchy item.</param>
        /// <param name="isDirty">true if the hierarchy item changed.</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
        public virtual int IsItemDirty(uint itemId, IntPtr docData, out int isDirty) 
        {

            IVsPersistDocData pd = (IVsPersistDocData)Marshal.GetObjectForIUnknown(docData);
            return ErrorHandler.ThrowOnFailure(pd.IsDocDataDirty(out isDirty));
        }

        /// <summary>
        /// Flag indicating that changes to a file can be ignored when item is saved or reloaded. 
        /// </summary>
        /// <param name="itemId">Specifies the item id from VSITEMID.</param>
        /// <param name="ignoreFlag">Flag indicating whether or not to ignore changes (1 to ignore, 0 to stop ignoring).</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
        public virtual int IgnoreItemFileChanges(uint itemId, int ignoreFlag) {
            HierarchyNode n = this.NodeFromItemId(itemId);
            if (n != null) {
                n.IgnoreItemFileChanges(ignoreFlag == 0 ? false : true);
            }

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Called to determine whether a project item is reloadable before calling ReloadItem. 
        /// </summary>
        /// <param name="itemId">Item identifier of an item in the hierarchy. Valid values are VSITEMID_NIL, VSITEMID_ROOT and VSITEMID_SELECTION.</param>
        /// <param name="isReloadable">A flag indicating that the project item is reloadable (1 for reloadable, 0 for non-reloadable).</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Reloadable")]
        public virtual int IsItemReloadable(uint itemId, out int isReloadable) {
            isReloadable = 0;

            HierarchyNode n = this.NodeFromItemId(itemId);
            if (n != null) {
                isReloadable = (n.IsItemReloadable()) ? 1 : 0;
            }

            return VSConstants.S_OK;
        }

        /// <summary>
        /// Called to reload a project item. 
        /// </summary>
        /// <param name="itemId">Specifies itemid from VSITEMID.</param>
        /// <param name="reserved">Reserved.</param>
        /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
        public virtual int ReloadItem(uint itemId, uint reserved) {
            HierarchyNode n = this.NodeFromItemId(itemId);
            if (n != null) {
                n.ReloadItem(reserved);
            }

            return VSConstants.S_OK;
        }

        #endregion

        public void UpdatePathForDeferredSave(string oldPath, string newPath) {
            var existing = _diskNodes[oldPath];
            _diskNodes.Remove(oldPath);
            _diskNodes.Add(newPath, existing);
        }

        #region IVsSetTargetFrameworkWorkerCallback

        public int UpdateTargetFramework(IVsHierarchy pHier, string currentTargetFramework, string newTargetFramework)
        {
            FrameworkName moniker = new FrameworkName(newTargetFramework);
            SetProjectProperty("TargetFrameworkIdentifier", moniker.Identifier);
            SetProjectProperty("TargetFrameworkVersion", "v" + moniker.Version);
            SetProjectProperty("TargetFrameworkProfile", moniker.Profile);
            return VSConstants.S_OK;
        }

        #endregion

        /*public virtual void OnTargetFrameworkMonikerChanged(FrameworkName currentTargetFramework, FrameworkName newTargetFramework)
        {
            if (currentTargetFramework == null)
            {
                throw new ArgumentNullException("currentTargetFramework");
            }
            if (newTargetFramework == null)
            {
                throw new ArgumentNullException("newTargetFramework");
            }

            var retargetingService = this.site.GetService(typeof(SVsTrackProjectRetargeting)) as IVsTrackProjectRetargeting;
            if (retargetingService == null)
            {
                // Probably in a unit test.
                ////throw new InvalidOperationException("Unable to acquire the SVsTrackProjectRetargeting service.");
                Marshal.ThrowExceptionForHR(UpdateTargetFramework(this.GetOuterHierarchy(), currentTargetFramework.FullName, newTargetFramework.FullName));
            }
            else
            {
                Marshal.ThrowExceptionForHR(retargetingService.OnSetTargetFramework(this.GetOuterHierarchy(), currentTargetFramework.FullName, newTargetFramework.FullName, this, true));
            }
        }

        public int UpgradeProject(uint grfUpgradeFlags)
        {
            int hr = VSConstants.S_OK;

            if (!PerformTargetFrameworkCheck(this.TargetFrameworkMoniker.FullName))
            {
                // Just return OLE_E_PROMPTSAVECANCELLED here which will cause the shell
                // to leave the project in an unloaded state.
                hr = VSConstants.OLE_E_PROMPTSAVECANCELLED;
            }

            return hr;
        }

        public bool PerformTargetFrameworkCheck(string newFrameworkName)
        {
            if (this.IsFrameworkOnMachine(newFrameworkName))
            {
                // Nothing to do since the framework is present.
                return true;
            }

            return ShowRetargetingDialog(newFrameworkName);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>
        /// <c>true</c> if the project will be retargeted.  <c>false</c> to load project in unloaded state.
        /// </returns>
        private bool ShowRetargetingDialog(string newFrameworkName)
        {
            var retargetDialog = this.site.GetService(typeof(SVsFrameworkRetargetingDlg)) as IVsFrameworkRetargetingDlg;
            if (retargetDialog == null)
            {
                throw new InvalidOperationException("Missing SVsFrameworkRetargetingDlg service.");
            }

            // We can only display the retargeting dialog if the IDE is not in command-line mode.
            if (IsIdeInCommandLineMode)
            {
                string message = SR.GetString(SR.CannotLoadUnknownTargetFrameworkProject, this.FileName, this.TargetFrameworkMoniker);
                var outputWindow = this.site.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
                if (outputWindow != null)
                {
                    IVsOutputWindowPane outputPane;
                    Guid outputPaneGuid = VSConstants.GUID_BuildOutputWindowPane;
                    if (outputWindow.GetPane(ref outputPaneGuid, out outputPane) >= 0 && outputPane != null)
                    {
                        Marshal.ThrowExceptionForHR(outputPane.OutputString(message));
                    }
                }

                throw new InvalidOperationException(message);
            }
            else
            {
                uint outcome;
                int dontShowAgain;
                Marshal.ThrowExceptionForHR(retargetDialog.ShowFrameworkRetargetingDlg(
                    this.ProjectMgr.ProjectType,
                    this.FileName,
                    newFrameworkName,
                    (uint)__FRD_FLAGS.FRDF_DEFAULT,
                    out outcome,
                    out dontShowAgain));
                switch ((__FRD_OUTCOME)outcome)
                {
                    case __FRD_OUTCOME.FRDO_GOTO_DOWNLOAD_SITE:
                        Marshal.ThrowExceptionForHR(retargetDialog.NavigateToFrameworkDownloadUrl());
                        return false;
                    case __FRD_OUTCOME.FRDO_LEAVE_UNLOADED:
                        return false;
                    case __FRD_OUTCOME.FRDO_RETARGET_TO_40:
                        // If we are retargeting to 4.0, then set the flag to set the appropriate Target Framework.
                        // This will dirty the project file, so we check it out of source control now -- so that
                        // the user can associate getting the checkout prompt with the "No Framework" dialog.
                        if (QueryEditProjectFile(false))
                        {
                            var retargetingService = this.site.GetService(typeof(SVsTrackProjectRetargeting)) as IVsTrackProjectRetargeting;
                            if (retargetingService != null)
                            {
                                // We surround our batch retargeting request with begin/end because in individual project load
                                // scenarios the solution load context hasn't done it for us.
                                Marshal.ThrowExceptionForHR(retargetingService.BeginRetargetingBatch());
                                Marshal.ThrowExceptionForHR(retargetingService.BatchRetargetProject(this.GetOuterHierarchy(), newFrameworkName, true));
                                Marshal.ThrowExceptionForHR(retargetingService.EndRetargetingBatch());
                            }
                            else
                            {
                                // Just setting the moniker to null will allow the default framework (.NETFX 4.0) to assert itself.
                                this.TargetFrameworkMoniker = null;
                            }

                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    default:
                        throw new ArgumentException("Unexpected outcome from retargeting dialog.");
                }
            }
        }

        private bool IsFrameworkOnMachine(string newFrameworkMoniker)
        {
            var multiTargeting = this.site.GetService(typeof(SVsFrameworkMultiTargeting)) as IVsFrameworkMultiTargeting;
            Array frameworks;
            Marshal.ThrowExceptionForHR(multiTargeting.GetSupportedFrameworks(out frameworks));
            foreach (string fx in frameworks)
            {
                uint compat;
                int hr = multiTargeting.CheckFrameworkCompatibility(newFrameworkMoniker, fx, out compat);
                if (hr < 0)
                {
                    break;
                }

                if ((__VSFRAMEWORKCOMPATIBILITY)compat == __VSFRAMEWORKCOMPATIBILITY.VSFRAMEWORKCOMPATIBILITY_COMPATIBLE)
                {
                    return true;
                }
            }

            return false;
        }*/
    }
}