/// <summary>
        /// Get the built-in set of tokens supported by the handler helper.
        /// </summary>
        /// <returns>
        /// Returns a new dictionary with built-in tokens and their values.
        /// </returns>
        private IDictionary <string, string> CreateTokenReplacementValues()
        {
            Dictionary <string, string> extended = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase)
            {
                { "ServiceInstance.Name", this.HandlerContext.ServiceInstance.Name },
                { "ServiceInstance.InstanceId", this.HandlerContext.ServiceInstance.InstanceId },
                { "ProjectName", ConnectedServicesUtilities.GetProjectProperty(this.context.ProjectHierarchy, __VSHPROPID.VSHPROPID_Name) },
                { "vslcid", "0x{0:X}".FormatInvariantCulture(CultureInfo.CurrentUICulture.LCID) }
            };

            string defaultNamespace = this.context.ProjectHierarchy.GetDefaultNamespace();

            if (defaultNamespace != null)
            {
                extended.Add(AzureIoTHubConnectedServiceHandlerHelper.RootNamespaceKey, defaultNamespace);
                extended.Add("ProjectDefaultNamespace", defaultNamespace);
            }

            Project  project = ConnectedServicesUtilities.GetDteProject(this.context.ProjectHierarchy);
            Property assemblyNameProperty = project.Properties.OfType <Property>().FirstOrDefault(p => string.Equals(p.Name, "AssemblyName", StringComparison.OrdinalIgnoreCase));

            if (assemblyNameProperty != null)
            {
                string assemblyName = assemblyNameProperty.Value as string;
                if (!string.IsNullOrWhiteSpace(assemblyName))
                {
                    extended.Add("AssemblyName", assemblyName);
                }
            }

            // Tokens for service metadata.
            if (this.HandlerContext.ServiceInstance.Metadata != null)
            {
                foreach (var item in this.HandlerContext.ServiceInstance.Metadata)
                {
                    if (item.Value is string)
                    {
                        // use the indexer instead of "Add" in case the Metadata dictionary conflicts with
                        // any 'extended' tokens above.  The Metadata dictionary will overwrite the built-in value.
                        extended["ServiceInstance.{0}".FormatInvariantCulture(item.Key)] = (string)item.Value;
                    }
                }
            }

            return(extended);
        }
        /// <summary>
        /// Copy a file to a project relative path.
        /// </summary>
        /// <param name="projectHierarchy">
        /// The project where to add the file.
        /// </param>
        /// <param name="fileName">
        /// The path to the file to copy.
        /// </param>
        /// <param name="targetPath">
        /// The target path, including the filename.
        /// </param>
        /// <param name="addFileOptions">
        /// The options to use while coping the file.
        /// </param>
        private async Task AddFileToProjectInFolder(IVsHierarchy projectHierarchy, string fileName, string targetPath, AddFileOptions addFileOptions)
        {
            targetPath = AzureIoTHubConnectedServiceHandlerHelper.GetProjectRelativePath(projectHierarchy, targetPath);
            Project      project = ConnectedServicesUtilities.GetDteProject(projectHierarchy);
            ProjectItems items   = project.ProjectItems;

            fileName = await this.CopyFileAsync(fileName, targetPath);

            string fileToAdd      = ConnectedServicesUtilities.GetProjectFullPath(projectHierarchy, targetPath);
            string targetFileName = Path.GetFileName(fileToAdd);

            // Build the directory structure if it doesn't already exist.
            Directory.CreateDirectory(Path.GetDirectoryName(fileToAdd));

            // clone the AdditionalReplacementValues dictionary so we aren't modifying the original
            Dictionary <string, string> replacementValues = new Dictionary <string, string>(addFileOptions.AdditionalReplacementValues);

            ProjectItem item        = AzureIoTHubConnectedServiceHandlerHelper.GetNestedProjectItem(items, targetPath);
            bool        existOnDisk = File.Exists(fileToAdd);

            if (item == null &&
                existOnDisk)
            {
                // The file is not in the project. We should add the file.
                // This is some arbitrary file, which we'll update in the same
                // path as existing project files.
                // This is 'fileToAdd' because we're not adding the final file here.
                item = items.AddFromFile(fileToAdd);
            }

            if (item != null)
            {
                // Add the folder-specific RootNamespace replacement value so $RootNamespace$ has the folder structure in it for C# projects
                this.AddRootNamespaceReplacementValue(replacementValues, item.Collection);

                bool filesEqual = this.AreFilesEqualWithReplacement(item, fileName, replacementValues);

                if (!filesEqual)
                {
                    if (!addFileOptions.SuppressOverwritePrompt && !this.PromptOverwrite(targetFileName))
                    {
                        // The user chose not to overwrite the file, so abort adding this file.
                        return;
                    }

                    // Get the document and overwrite with file content.
                    BufferUtilities.UpdateProjectItemFromFile(item, fileName);
                }
            }
            else
            {
                File.Copy(fileName, fileToAdd);
                item = items.AddFromFile(fileToAdd);

                // Add the folder-specific RootNamespace replacement value so $RootNamespace$ has the folder structure in it for C# projects
                this.AddRootNamespaceReplacementValue(replacementValues, item.Collection);
            }

            this.PerformTokenReplacement(item, replacementValues);

            if (addFileOptions.OpenOnComplete && !item.IsOpen)
            {
                try
                {
                    var window = item.Open();

                    // Ensure that the window is always shown regardless of "Preview"
                    // user settings.
                    if (window != null &&
                        !window.Visible)
                    {
                        window.Visible = true;
                    }
                }
                catch (Exception)
                {
                }
            }
        }