/// <summary>
        ///  Load circuit templates stored in an external file</summary>
        /// <param name="uri">Document URI, or null to present file open dialog to user</param>
        /// <returns>Returns the file path used to load the external templates.
        /// An empty string indicates no templates were loaded</returns>
        protected override ImportedContent LoadExternalTemplateLibrary(Uri uri)
        {
            string filePath = string.Empty;

            if (uri == null)
            {
                var dlg = new OpenFileDialog();
                dlg.Filter          = "Script Template File (*.vscript)|*.vscript".Localize();
                dlg.CheckFileExists = true;
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    uri      = new Uri(dlg.FileName, UriKind.RelativeOrAbsolute);
                    filePath = dlg.FileName;
                }
            }
            else
            {
                filePath = uri.LocalPath;
            }

            if (File.Exists(filePath))
            {
                if (TemplatingContext.ValidateNewFolderUri(uri))
                {
                    // read existing document using standard XML reader
                    using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                    {
                        var reader   = new ScriptReader(m_nodeDefinitionManager.NodeTypeManager);
                        var root     = reader.Read(stream, uri);
                        var toFolder = CreateTemplateFolder();
                        reader.ImportTemplates(toFolder.DomNode, root, uri);
                        return(new ImportedContent(toFolder.DomNode, uri));
                    }
                }
            }

            return(new ImportedContent(null, null));
        }
        /// <summary>
        /// Rescan all referenced template documents </summary>
        protected override void ReloadExternalTemplates()
        {
            // 1st, scan the template hierarchy tree and remember all the existing template's guid -> DomNode map
            var oldNodeGuidDictionary = new Dictionary <string, DomNode>();

            foreach (var node in TemplatingContext.RootFolder.DomNode.Subtree)
            {
                if (node.Is <ScriptTemplate>())
                {
                    var template = node.Cast <ScriptTemplate>();
                    if (template.Guid != Guid.Empty)
                    {
                        oldNodeGuidDictionary[template.Guid.ToString()] = node;
                    }
                }
            }

            // 2nd, scan the template hierarchy tree again and reload all the external templates
            var newNodeGuidDictionary = new Dictionary <string, DomNode>();

            foreach (var node in TemplatingContext.RootFolder.DomNode.Subtree)
            {
                var templateFolder = node.As <Sce.Atf.Dom.TemplateFolder>();
                if (templateFolder == null || templateFolder.Url == null)
                {
                    continue;
                }
                if (File.Exists(templateFolder.Url.LocalPath))
                {
                    using (FileStream stream = new FileStream(templateFolder.Url.LocalPath, FileMode.Open, FileAccess.Read))
                    {
                        var reader   = new ScriptReader(m_nodeDefinitionManager.NodeTypeManager);
                        var rootNode = reader.Read(stream, templateFolder.Url);

                        foreach (var newnode in rootNode.Subtree)
                        {
                            var guidAttr = newnode.Type.GetAttributeInfo("guid");
                            if (guidAttr == null)
                            {
                                continue;
                            }
                            var guidStr = newnode.GetAttribute(guidAttr) as string;
                            if (!string.IsNullOrEmpty(guidStr))
                            {
                                newNodeGuidDictionary[guidStr] = newnode;
                            }
                        }
                        reader.ImportTemplates(templateFolder.DomNode, rootNode, templateFolder.Url);
                    }
                }
            }

            // 3rd, replace original nodes with newly loaded, matched by GUIDs
            foreach (var node in TemplatingContext.RootFolder.DomNode.GetRoot().Subtree)
            {
                // currently two types that reference templates: GroupInstance or ModuleInstance
                DomNode refNode;

                var groupInstance = node.As <VisualScript.ScriptGroupReference>();
                if (groupInstance != null)
                {
                    if (newNodeGuidDictionary.TryGetValue(groupInstance.Template.Guid.ToString(), out refNode))
                    {
                        groupInstance.Template = refNode.As <ScriptTemplate>();

                        // need to reset pin targets due to DomNode replacements
                        var graphContainer = groupInstance.DomNode.Parent.Cast <ICircuitContainer>();
                        foreach (var edge in graphContainer.Wires)
                        {
                            if (edge.OutputElement.DomNode == groupInstance.DomNode)
                            {
                                edge.OutputPinTarget = null;
                            }

                            if (edge.InputElement.DomNode == groupInstance.DomNode)
                            {
                                edge.InputPinTarget = null;
                            }
                        }
                        continue;
                    }
                }

                var moduleReference = node.As <ScriptNodeReference>();
                if (moduleReference != null)
                {
                    if (newNodeGuidDictionary.TryGetValue(moduleReference.Template.Guid.ToString(), out refNode))
                    {
                        moduleReference.Template = refNode.As <ScriptTemplate>();

                        // need to reset pin targets due to DomNode replacements
                        var graphContainer = moduleReference.DomNode.Parent.Cast <ICircuitContainer>();
                        foreach (var edge in graphContainer.Wires)
                        {
                            if (edge.OutputElement.DomNode == moduleReference.DomNode)
                            {
                                edge.OutputPinTarget = null;
                            }

                            if (edge.InputElement.DomNode == moduleReference.DomNode)
                            {
                                edge.InputPinTarget = null;
                            }
                        }
                    }
                }
            }

            // finally, prune the old nodes
            foreach (var keyValue in newNodeGuidDictionary)
            {
                DomNode oldNode;
                if (oldNodeGuidDictionary.TryGetValue(keyValue.Key, out oldNode))
                {
                    oldNode.RemoveFromParent();
                }
            }
        }