/// <summary>
        /// This function is the callback used to execute the command when the menu item is clicked.
        /// See the constructor to see how the menu item is associated with this function using
        /// OleMenuCommandService service and MenuCommand class.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void Execute(object sender, EventArgs e)
        {
            // Verify the current thread is the UI thread.
            ThreadHelper.ThrowIfNotOnUIThread();

            string name      = FormatXmlCommand.environment.ActiveDocument.FullName;
            string extension = Path.GetExtension(FormatXmlCommand.environment.ActiveDocument.FullName).ToUpperInvariant();

            if (FormatXmlCommand.XmlExtensions.Contains(extension))
            {
                // Get the selected text from the environment and make a note of where we are in the document.
                TextSelection selection = FormatXmlCommand.environment.ActiveDocument.Selection as TextSelection;
                int           line      = selection.ActivePoint.Line;
                int           offset    = selection.ActivePoint.LineCharOffset;

                // Get the start end points (round down and up to the start of a line).
                EditPoint startPoint = selection.AnchorPoint.CreateEditPoint();
                startPoint.StartOfDocument();

                // The initial endpoint is one line below the start.
                EditPoint endPoint = selection.ActivePoint.CreateEditPoint();
                endPoint.EndOfDocument();

                // This will replace the XML in the current module with the scrubbed and beautified XML.
                startPoint.ReplaceText(
                    endPoint,
                    FormatXmlCommand.ScrubDocument(startPoint.GetText(endPoint), extension),
                    (int)(vsEPReplaceTextOptions.vsEPReplaceTextNormalizeNewlines | vsEPReplaceTextOptions.vsEPReplaceTextTabsSpaces));

                // There will likely be some dramatic changes to the format of the document.  This will restore the cursor to where it was in the
                // document before we beautified it.
                selection.MoveToLineAndOffset(line, offset);
            }
        }
        /// <summary>
        /// Orders and beautifies an XML document.
        /// </summary>
        /// <param name="source">The source XML document to order and beautify.</param>
        /// <param name="extension">The extension.  Used to determine tab spacing.</param>
        /// <returns>The text of the scrubbed and beautified document.</returns>
        private static string ScrubDocument(string source, string extension)
        {
            // Used to write the final document in the form of a long string.
            using (StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture))
            {
                try
                {
                    // Read the source document.
                    using (StringReader stringReader = new StringReader(source))
                    {
                        // Create an XML document from the source string.
                        XDocument sourceDocument = XDocument.Load(stringReader, LoadOptions.PreserveWhitespace);

                        // Create a new target document.
                        XDocument targetDocument = new XDocument();

                        // Copy the declaration.
                        if (sourceDocument.Declaration != null)
                        {
                            targetDocument.Declaration = new XDeclaration(sourceDocument.Declaration);
                        }

                        // This will order all the attributes in the document in alphabetical order.  Note that the elements can't be similarly
                        // ordered because this could produce forward reference errors.
                        FormatXmlCommand.RecurseIntoDocument(sourceDocument.Root, targetDocument);

                        // This is used to make special consideration for XALM files.
                        bool isXaml = extension == ".XAML";

                        // Beautify and save the target document when it has been ordered.
                        XmlFormattedWriterSettings xmlFormattedWriterSettings = new XmlFormattedWriterSettings
                        {
                            Encoding           = Encoding.UTF8,
                            OmitXmlDeclaration = isXaml,
                            TabSize            = isXaml ? 4 : 2,
                        };
                        XmlWriter xmlWriter = XmlFormattedWriter.Create(stringWriter, xmlFormattedWriterSettings);
                        targetDocument.WriteTo(xmlWriter);
                        xmlWriter.Close();
                    }
                }
                catch (Exception exception)
                {
                    // Show a message box to prove we were here
                    VsShellUtilities.ShowMessageBox(
                        FormatXmlCommand.serviceProvider,
                        exception.Message,
                        Resources.EditorExtensionsTitle,
                        OLEMSGICON.OLEMSGICON_CRITICAL,
                        OLEMSGBUTTON.OLEMSGBUTTON_OK,
                        OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
                }

                // The result of beautifying a long string of XML is another long string that is scrubbed and beautified.  This string will be used
                // to replace the original content of the module.
                return(stringWriter.ToString());
            }
        }
        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        /// <returns>An awaitable task.</returns>
        public static async Task InitializeAsync(AsyncPackage package)
        {
            // Execute this method on the UI thread.
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            // Instantiate the command.
            OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;

            FormatXmlCommand.instance = new FormatXmlCommand(package, commandService);
        }
        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initialization code that rely on services provided by VisualStudio.
        /// </summary>
        /// <param name="cancellationToken">A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.</param>
        /// <param name="progress">A provider for progress updates.</param>
        /// <returns>A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.</returns>
        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress <ServiceProgressData> progress)
        {
            // When initialized asynchronously, the current thread may be a background thread at this point.
            // Do any initialization that requires the UI thread after switching to the UI thread.
            await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

            await FormatCommentCommand.InitializeAsync(this);

            await FormatXmlCommand.InitializeAsync(this);

            await InsertModuleHeaderCommand.InitializeAsync(this);

            await InsertConstructorHeaderCommand.InitializeAsync(this);

            await SetModuleHeaderCommand.InitializeAsync(this);

            await SetWrapMarginCommand.InitializeAsync(this);

            await ScrubXsdCommand.InitializeAsync(this);
        }
        /// <summary>
        /// Sorts the attributes of an element and all it's children in alphabetical order.
        /// </summary>
        /// <param name="sourceNode">The original XElement.</param>
        /// <param name="targetContainer">The target XElement where the newly sorted node is placed.</param>
        private static void RecurseIntoDocument(XNode sourceNode, XContainer targetContainer)
        {
            // Convert the generic node to a specific type.
            XElement sourceElement = sourceNode as XElement;

            if (sourceElement != null)
            {
                // Arrange all the attributes in alphabetical order.
                SortedDictionary <string, XAttribute> sortedDictionary = new SortedDictionary <string, XAttribute>();
                foreach (XAttribute attribute in sourceElement.Attributes())
                {
                    sortedDictionary.Add(attribute.Name.LocalName, attribute);
                }

                // Create a new target element from the source and remove the existing attributes.  They'll be replaced with the ordered list.
                XElement element = new XElement(sourceElement);
                element.RemoveAll();

                // Replace the attributes in alphabetical order.
                foreach (KeyValuePair <string, XAttribute> keyValuePair in sortedDictionary)
                {
                    element.Add(keyValuePair.Value);
                }

                // Add it back to the target document.
                targetContainer.Add(element);

                // Recurse into each of the child elements.
                foreach (XNode childNode in sourceElement.Nodes())
                {
                    FormatXmlCommand.RecurseIntoDocument(childNode, element);
                }
            }
            else
            {
                // Pass all other nodes into the output without alterations.
                targetContainer.Add(sourceNode);
            }
        }