コード例 #1
0
ファイル: IfThenComponent.cs プロジェクト: zyj0021/SHFB
        /// <inheritdoc />
        public override void Apply(XmlDocument document, string key)
        {
            // Set up the test
            CustomContext context = new CustomContext(contextNamespaces);

            context["key"] = key;

            XPathExpression test = condition.Clone();

            test.SetContext(context);

            // Evaluate the condition
            bool result = (bool)document.CreateNavigator().Evaluate(test);

            // On the basis of the condition, execute either the true or the false branch
            if (result)
            {
                foreach (BuildComponentCore component in trueBranch)
                {
                    component.Apply(document, key);
                }
            }
            else
            {
                foreach (BuildComponentCore component in falseBranch)
                {
                    component.Apply(document, key);
                }
            }
        }
コード例 #2
0
        /// <inheritdoc />
        public override void Apply(XmlDocument document, string key)
        {
            // Set the context
            CustomContext context = new CustomContext(contextNamespaces);

            context["key"] = key;

            // Evaluate the condition
            XPathExpression xPathLocal = xPath.Clone();

            xPathLocal.SetContext(context);

            object result = document.CreateNavigator().Evaluate(xPathLocal);

            // Try to interpret the result as a node set
            XPathNodeIterator resultNodeIterator = result as XPathNodeIterator;

            if (resultNodeIterator != null)
            {
                // If it is, apply the child components to each node value
                foreach (XPathNavigator resultNode in resultNodeIterator.ToArray())
                {
                    ApplyComponents(document, resultNode.Value);
                }
            }
            else
            {
                // If it isn't, apply the child components to the string value of the result
                ApplyComponents(document, result.ToString());
            }
        }
コード例 #3
0
        /// <inheritdoc />
        public override void Apply(XmlDocument document, string key)
        {
            // adjust the context
            context["key"] = key;

            // evaluate the condition
            XPathExpression xpath_local = xpath.Clone();

            xpath_local.SetContext(context);

            Object result = document.CreateNavigator().Evaluate(xpath_local);

            // try to interpret the result as a node set
            XPathNodeIterator result_node_iterator = result as XPathNodeIterator;

            if (result_node_iterator != null)
            {
                // if it is, apply the child components to each node value
                foreach (XPathNavigator result_node in result_node_iterator.ToArray())
                {
                    ApplyComponents(document, result_node.Value);
                }
            }
            else
            {
                // if it isn't, apply the child components to the string value of the result
                ApplyComponents(document, result.ToString());
            }
        }
コード例 #4
0
        /// <summary>
        /// Returns the string result from evaluating an xpath expression against the given document and context.
        /// </summary>
        public static string EvalXPathExpr(IXPathNavigable doc, XPathExpression xpe, CustomContext c)
        {
            XPathExpression t = xpe.Clone();

            t.SetContext(c);
            return(doc.CreateNavigator().Evaluate(t).ToString());
        }
コード例 #5
0
ファイル: ForEachComponent.cs プロジェクト: terry2012/DSV
        // the work of the component

        public override void Apply(XmlDocument document, string key)
        {
            // adjust the context
            context["key"] = key;

            // evaluate the condition
            XPathExpression xpath_local = xpath.Clone();

            xpath_local.SetContext(context);

            Object result = document.CreateNavigator().Evaluate(xpath_local);

            // try to intrepret the result as a node set
            XPathNodeIterator result_node_iterator = result as XPathNodeIterator;

            if (result_node_iterator != null)
            {
                XPathNavigator[] result_nodes = BuildComponentUtilities.ConvertNodeIteratorToArray(result_node_iterator);
                //Console.WriteLine("{0} node-set result", result_nodes.Length);
                // if it is, apply the child components to each node value
                foreach (XPathNavigator result_node in result_nodes)
                {
                    // Console.WriteLine(result_node.Value);
                    ApplyComponents(document, result_node.Value);
                }
            }
            else
            {
                //Console.WriteLine("non-node-set result");
                // if it isn't, apply the child components to the string value of the result
                ApplyComponents(document, result.ToString());
            }
        }
コード例 #6
0
        internal void LoadCompiledStylesheet(out Stylesheet compiledStylesheet, out XPathExpression[] querylist, out ArrayList queryStore, out RootAction rootAction, XPathNavigator input)
        {
            //
            // Extract the state atomically
            //
            if (_CompiledStylesheet == null || _QueryStore == null || _RootAction == null)
            {
                throw new XsltException(Res.Xslt_NoStylesheetLoaded);
            }

            compiledStylesheet = _CompiledStylesheet;
            queryStore         = _QueryStore;
            rootAction         = _RootAction;
            int queryCount = _QueryStore.Count;

            querylist = new XPathExpression[queryCount]; {
                bool canClone = input is DocumentXPathNavigator || input is XPathDocumentNavigator;
                for (int i = 0; i < queryCount; i++)
                {
                    XPathExpression query = ((TheQuery)_QueryStore[i]).CompiledQuery;
                    if (canClone)
                    {
                        querylist[i] = query.Clone();
                    }
                    else
                    {
                        bool   hasPrefix;
                        IQuery ast = new QueryBuilder().Build(query.Expression, out hasPrefix);
                        querylist[i] = new CompiledXpathExpr(ast, query.Expression, hasPrefix);
                    }
                }
            }
        }
コード例 #7
0
        /// <summary>
        /// This is used to get the string result from evaluating an XPath expression against the given
        /// document and context.
        /// </summary>
        /// <param name="document">The document to use</param>
        /// <param name="expression">The XPath expression to evaluate</param>
        /// <param name="context">The context to use</param>
        /// <returns>The evaluated expression result</returns>
        /// <overloads>There are two overloads for this method</overloads>
        public static string EvalXPathExpr(this IXPathNavigable document, XPathExpression expression,
                                           CustomContext context)
        {
            XPathExpression t = expression.Clone();

            t.SetContext(context);

            return(document.CreateNavigator().Evaluate(t).ToString());
        }
コード例 #8
0
        /// <summary>
        /// Selects a node set, using the specified XPath expression.
        /// </summary>
        /// <param name="navigator">A source XPathNavigator.</param>
        /// <param name="expression">An XPath expression.</param>
        /// <param name="variables">A set of XPathVariables.</param>
        /// <returns>An iterator over the nodes matching the specified expression.</returns>
        public static XPathNodeIterator Select(this XPathNavigator navigator, XPathExpression expression, params XPathVariable[] variables)
        {
            if (variables == null || variables.Length == 0 || variables[0] == null)
                return navigator.Select(expression);

            var compiled = expression.Clone(); // clone for thread-safety
            var context = new DynamicContext();
            foreach (var variable in variables)
                context.AddVariable(variable.Name, variable.Value);
            compiled.SetContext(context);
            return navigator.Select(compiled);
        }
コード例 #9
0
ファイル: XPathCache.cs プロジェクト: zanyants/mvp.xml
        /// <summary>
        /// Retrieves a cached compiled expression, or a newly compiled one.
        /// </summary>
        private static XPathExpression GetCompiledExpression(string expression, XPathNavigator source)
        {
            XPathExpression expr = (XPathExpression)Cache[expression];

            // No double checks. At most we will compile twice. No big deal.
            if (expr == null)
            {
                expr = source.Compile(expression);
                Cache[expression] = expr;
            }

            return(expr.Clone());
        }
コード例 #10
0
ファイル: XPathContext.cs プロジェクト: zwlstc109/CleanAOP
 public bool Evaluate(XPathExpression xpath, XPathNavigator source, out object result)
 {
     xpath = (XPathExpression)xpath.Clone();
     xpath.SetContext(new XPathSemantics(this));
     result = source.Evaluate(xpath);
     if (xpath.ReturnType == XPathResultType.NodeSet)
     {
         if (((XPathNodeIterator)result).Count == 0)
         {
             result = null;
         }
     }
     return(result != null);
 }
コード例 #11
0
 /// <summary>
 /// Implements the following function
 /// node-set exsl:node-set(object)
 /// </summary>
 /// <param name="o"></param>
 /// <returns></returns>
 public XPathNodeIterator nodeSet(object o)
 {
     if (o is XPathNavigator)
     {
         XPathNavigator nav = (XPathNavigator)o;
         if (selectItself == null)
         {
             selectItself = nav.Compile(".");
         }
         return(nav.Select(selectItself.Clone()));
     }
     else if (o is XPathNodeIterator)
     {
         return(o as XPathNodeIterator);
     }
     else
     {
         string s;
         if (o is string)
         {
             s = o as string;
         }
         else if (o is bool)
         {
             s = ((bool)o)? "true" : "false";
         }
         else if (o is Double || o is Int16 || o is UInt16 || o is Int32 ||
                  o is UInt32 || o is Int64 || o is UInt64 || o is Single || o is Decimal)
         {
             s = o.ToString();
         }
         else
         {
             return(null);
         }
         //Now convert it to text node
         XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.None);
         XPathDocument    doc     = new XPathDocument(
             new XmlTextReader("<d>" + s + "</d>", XmlNodeType.Element, context));
         XPathNavigator nav = doc.CreateNavigator();
         if (selectText == null)
         {
             selectText = nav.Compile("/d/text()");
         }
         return(nav.Select(selectText.Clone()));
     }
 }
コード例 #12
0
        private XPathExpression GetPersonAppSettingsXPathExpression(XPathNavigator infoNav)
        {
            XmlNamespaceManager infoXmlNamespaceManager = new XmlNamespaceManager(infoNav.NameTable);

            infoXmlNamespaceManager.AddNamespace("wc", "urn:com.microsoft.wc.methods.response.GetApplicationSettings");

            XPathExpression infoPersonAppSettingsPathClone;

            lock (s_infoPersonAppSettingsPath)
            {
                infoPersonAppSettingsPathClone = s_infoPersonAppSettingsPath.Clone();
            }

            infoPersonAppSettingsPathClone.SetContext(infoXmlNamespaceManager);

            return(infoPersonAppSettingsPathClone);
        }
コード例 #13
0
        private XPathExpression GetRecordXPathExpression(XPathNavigator infoNav)
        {
            XmlNamespaceManager infoXmlNamespaceManager = new XmlNamespaceManager(infoNav.NameTable);

            infoXmlNamespaceManager.AddNamespace("wc", "urn:com.microsoft.wc.methods.response.GetAuthorizedRecords");

            XPathExpression infoRecordPathClone;

            lock (s_infoRecordPath)
            {
                infoRecordPathClone = s_infoRecordPath.Clone();
            }

            infoRecordPathClone.SetContext(infoXmlNamespaceManager);

            return(infoRecordPathClone);
        }
コード例 #14
0
        /// <summary>
        /// This is used to get the string result from evaluating an XPath expression against the given
        /// document and context.
        /// </summary>
        /// <param name="document">The document to use</param>
        /// <param name="expression">The XPath expression to evaluate</param>
        /// <param name="context">The context to use</param>
        /// <returns>The evaluated expression result</returns>
        /// <overloads>There are two overloads for this method</overloads>
        public static string EvalXPathExpr(this IXPathNavigable document, XPathExpression expression,
                                           CustomContext context)
        {
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }

            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            XPathExpression t = expression.Clone();

            t.SetContext(context);

            return(document.CreateNavigator().Evaluate(t).ToString());
        }
        internal static XPathExpression GetQueryPermissionsInfoXPathExpression(XPathNavigator infoNav)
        {
            XmlNamespaceManager infoXmlNamespaceManager =
                new XmlNamespaceManager(infoNav.NameTable);

            infoXmlNamespaceManager.AddNamespace(
                "wc",
                "urn:com.microsoft.wc.methods.response.QueryPermissions");

            XPathExpression infoPathClone;

            lock (s_queryPermissionsInfoPath)
            {
                infoPathClone = s_queryPermissionsInfoPath.Clone();
            }

            infoPathClone.SetContext(infoXmlNamespaceManager);

            return(infoPathClone);
        }
コード例 #16
0
        internal static XPathExpression GetInfoXPathExpression(
            string methodNSSuffix, XPathNavigator infoNav)
        {
            XmlNamespaceManager infoXmlNamespaceManager =
                new XmlNamespaceManager(infoNav.NameTable);

            infoXmlNamespaceManager.AddNamespace(
                "wc",
                "urn:com.microsoft.wc.methods.response." + methodNSSuffix);

            XPathExpression infoPathClone = null;

            lock (s_infoPath)
            {
                infoPathClone = s_infoPath.Clone();
            }

            infoPathClone.SetContext(infoXmlNamespaceManager);

            return(infoPathClone);
        }
        private static XPathExpression GetPersonXPathExpression(
            XPathNavigator infoNav)
        {
            XmlNamespaceManager infoXmlNamespaceManager =
                new XmlNamespaceManager(infoNav.NameTable);

            infoXmlNamespaceManager.AddNamespace(
                "wc",
                "urn:com.microsoft.wc.methods.response.GetPersonInfo");

            XPathExpression infoPersonPathClone = null;

            lock (s_infoPersonPath)
            {
                infoPersonPathClone = s_infoPersonPath.Clone();
            }

            infoPersonPathClone.SetContext(infoXmlNamespaceManager);

            return(infoPersonPathClone);
        }
コード例 #18
0
ファイル: XmlHelper.cs プロジェクト: yuzs/CodePorter
        public static XPathNodeIterator SelectNodes(XPathNavigator navigator, string xpath, ref XPathExpression compiledExpression, bool multiThreaded)
        {
            if (navigator == null)
            {
                throw new ArgumentNullException("navigator");
            }
            if (xpath == null)
            {
                throw new ArgumentNullException("xpath");
            }
            XPathExpression expression = compiledExpression;

            if (expression == null)
            {
                expression         = navigator.Compile(xpath);
                compiledExpression = expression;
            }
            else if (multiThreaded)
            {
                expression = expression.Clone();
            }
            return(navigator.Select(expression));
        }
コード例 #19
0
        public TargetInfo GetTargetInfo(XPathNavigator linkNode, CustomContext context)
        {
            // compute the metadata file name to open
            XPathExpression localFileExpression = fileExpression.Clone();

            localFileExpression.SetContext(context);
            string file = linkNode.Evaluate(localFileExpression).ToString();

            if (String.IsNullOrEmpty(file))
            {
                return(null);
            }

            // load the metadata file
            XPathDocument metadataDocument = GetDocument(file);

            if (metadataDocument == null)
            {
                return(null);
            }

            // querry the metadata file for the target url and link text
            XPathNavigator  metadataNode       = metadataDocument.CreateNavigator();
            XPathExpression localUrlExpression = urlExpression.Clone();

            localUrlExpression.SetContext(context);
            string          url = metadataNode.Evaluate(localUrlExpression).ToString();
            XPathExpression localTextExpression = textExpression.Clone();

            localTextExpression.SetContext(context);
            string text = metadataNode.Evaluate(localTextExpression).ToString();

            // return this information
            TargetInfo info = new TargetInfo(url, text, type);

            return(info);
        }
コード例 #20
0
        public void Apply(XmlDocument targetDocument, IXmlNamespaceResolver context)
        {
            XPathExpression local_file_expression = file_expression.Clone();

            local_file_expression.SetContext(context);

            XPathExpression local_source_expression = source_expression.Clone();

            local_source_expression.SetContext(context);

            XPathExpression local_target_expression = target_expression.Clone();

            local_target_expression.SetContext(context);

            string file_name = (string)targetDocument.CreateNavigator().Evaluate(local_file_expression);
            string file_path = Path.Combine(root_directory, file_name);

            if (!File.Exists(file_path))
            {
                return;
            }
            XPathDocument sourceDocument = new XPathDocument(file_path);

            XPathNavigator target_node = targetDocument.CreateNavigator().SelectSingleNode(local_target_expression);

            if (target_node == null)
            {
                return;
            }

            XPathNodeIterator source_nodes = sourceDocument.CreateNavigator().Select(local_source_expression);

            foreach (XPathNavigator source_node in source_nodes)
            {
                target_node.AppendChild(source_node);
            }
        }
コード例 #21
0
        /// <inheritdoc />
        public override void Apply(XmlDocument document, string key)
        {
            // Set the evaluation context
            context["key"] = key;

            XPathExpression xpath = pathExpression.Clone();

            xpath.SetContext(context);

            // Evaluate the path
            string path = document.CreateNavigator().Evaluate(xpath).ToString();

            if (basePath != null)
            {
                path = Path.Combine(basePath, path);
            }

            string targetDirectory = Path.GetDirectoryName(path);

            if (!Directory.Exists(targetDirectory))
            {
                Directory.CreateDirectory(targetDirectory);
            }

            if (writeXhtmlNamespace)
            {
                document.DocumentElement.SetAttribute("xmlns", "http://www.w3.org/1999/xhtml");
                document.LoadXml(document.OuterXml);
            }

            if (settings.OutputMethod == XmlOutputMethod.Html)
            {
                XmlDocumentType documentType = document.CreateDocumentType("html", null, null, null);
                if (document.DocumentType != null)
                {
                    document.ReplaceChild(documentType, document.DocumentType);
                }
                else
                {
                    document.InsertBefore(documentType, document.FirstChild);
                }
            }

            // Save the document.

            // selectExpression determines which nodes get saved. If there is no selectExpression we simply
            // save the root node as before. If there is a selectExpression, we evaluate the XPath expression
            // and save the resulting node set. The select expression also enables the "literal-text" processing
            // instruction, which outputs its content as unescaped text.
            if (selectExpression == null)
            {
                try
                {
                    using (XmlWriter writer = XmlWriter.Create(path, settings))
                    {
                        document.Save(writer);
                    }
                }
                catch (IOException e)
                {
                    base.WriteMessage(key, MessageLevel.Error, "An access error occurred while attempting to " +
                                      "save to the file '{0}'. The error message is: {1}", path, e.GetExceptionMessage());
                }
                catch (XmlException e)
                {
                    base.WriteMessage(key, MessageLevel.Error, "Invalid XML was written to the output " +
                                      "file '{0}'. The error message is: '{1}'", path, e.GetExceptionMessage());
                }
            }
            else
            {
                // IMPLEMENTATION NOTE: The separate StreamWriter is used to maintain XML indenting.  Without it
                // the XmlWriter won't honor our indent settings after plain text nodes have been written.
                using (StreamWriter output = File.CreateText(path))
                {
                    using (XmlWriter writer = XmlWriter.Create(output, settings))
                    {
                        XPathExpression select_xpath = selectExpression.Clone();
                        select_xpath.SetContext(context);

                        XPathNodeIterator ni = document.CreateNavigator().Select(selectExpression);

                        while (ni.MoveNext())
                        {
                            if (ni.Current.NodeType == XPathNodeType.ProcessingInstruction &&
                                ni.Current.Name.Equals("literal-text"))
                            {
                                writer.Flush();
                                output.Write(ni.Current.Value);
                            }
                            else
                            {
                                ni.Current.WriteSubtree(writer);
                            }
                        }
                    }
                }
            }

            // Raise an event to indicate that a file was created
            this.OnComponentEvent(new FileCreatedEventArgs(path, true));
        }
コード例 #22
0
ファイル: SaveComponent.cs プロジェクト: thild/dotnetopenid
        public override void Apply(XmlDocument document, string key)
        {
            // set the evaluation context
            context["key"] = key;

            XPathExpression path_xpath = path_expression.Clone();

            path_xpath.SetContext(context);

            // evaluate the path
            string path = document.CreateNavigator().Evaluate(path_xpath).ToString();
            string file = Path.GetFileName(path);

            string fileLinkPath = Path.Combine(linkPath, file);

            if (basePath != null)
            {
                path = Path.Combine(basePath, path);
            }


            // *SECURIY* The path name may be derived from user entered meta data in Doc Studio and as such
            // it is not trustworthy. To pass, the path must be inside the directory tree base_directory
            // (which is the current directory if a path is not specified).

            // This test is causing problems

            /*
             * string absoluteBasePath = (basePath == null) ? Directory.GetCurrentDirectory() : Path.GetFullPath(basePath);
             * string targetPath = Path.GetDirectoryName(path);
             * if (!targetPath.StartsWith(absoluteBasePath, StringComparison.CurrentCultureIgnoreCase)) {
             *  WriteMessage(MessageLevel.Error, string.Format("Cannot save document outside of base directory: {0}", targetPath));
             *  return;
             * }
             */
            string targetDirectory = Path.GetDirectoryName(path);

            if (!Directory.Exists(targetDirectory))
            {
                Directory.CreateDirectory(targetDirectory);
            }

            // save the document
            // select_expression determines which nodes get saved. If there is no select_expression
            // we simply save the root node as before. If there is a select_expression, we evaluate the
            // xpath expression and save the resulting node set. The select expression also enables the
            // "literal-text" processing instruction, which outputs its content as unescaped text.
            if (select_expression == null)
            {
                XmlNode doctype = document.DocumentType;
                try {
                    //Console.WriteLine("path = '{0}'", path);
                    //document.Save(path);

                    using (XmlWriter writer = XmlWriter.Create(path, settings)) {
                        document.Save(writer);
                    }
                } catch (IOException e) {
                    WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to save to the file '{0}'. The error message is: {1}", path, BuildComponentUtilities.GetExceptionMessage(e)));
                } catch (XmlException e) {
                    WriteMessage(MessageLevel.Error, String.Format("Invalid XML was written to the output file '{0}'. The error message is: '{1}'", path, BuildComponentUtilities.GetExceptionMessage(e)));
                }

                // Get the relative html path for HXF generation.
                int    index    = fileLinkPath.IndexOf('/');
                string htmlPath = fileLinkPath.Substring(index + 1, fileLinkPath.Length - (index + 1));

                FileCreatedEventArgs fe = new FileCreatedEventArgs(htmlPath, Path.GetDirectoryName(targetDirectory));
                OnComponentEvent(fe);
            }
            else
            {
                // IMPLEMENTATION NOTE: The separate StreamWriter is used to maintain XML indenting.
                // Without it the XmlWriter won't honor our indent settings after plain text nodes have been
                // written.
                settings.ConformanceLevel = ConformanceLevel.Auto;
                using (StreamWriter output = File.CreateText(path)) {
                    using (XmlWriter writer = XmlWriter.Create(output, settings)) {
                        XPathExpression select_xpath = select_expression.Clone();
                        select_xpath.SetContext(context);
                        XPathNodeIterator ni = document.CreateNavigator().Select(select_expression);
                        while (ni.MoveNext())
                        {
                            if (ni.Current.NodeType == XPathNodeType.ProcessingInstruction && ni.Current.Name.Equals("literal-text"))
                            {
                                writer.Flush();
                                output.Write(ni.Current.Value);
                            }
                            else
                            {
                                ni.Current.WriteSubtree(writer);
                            }
                        }
                    }
                }
            }
        }
コード例 #23
0
ファイル: XPathContext.cs プロジェクト: zwlstc109/CleanAOP
 public bool Matches(XPathExpression xpath, XPathNavigator source)
 {
     xpath = (XPathExpression)xpath.Clone();
     xpath.SetContext(new XPathSemantics(this));
     return(source.Matches(xpath));
 }
コード例 #24
0
ファイル: XPathContext.cs プロジェクト: zwlstc109/CleanAOP
 public XPathNavigator SelectSingleNode(XPathExpression xpath, XPathNavigator source)
 {
     xpath = (XPathExpression)xpath.Clone();
     xpath.SetContext(new XPathSemantics(this));
     return(source.SelectSingleNode(xpath));
 }
コード例 #25
0
ファイル: SaveComponent.cs プロジェクト: Tyler-Zhou/SHFB
        //=====================================================================

        /// <summary>
        /// This is used to write the document to its destination file
        /// </summary>
        private void WriteDocuments()
        {
            string lastKey = "??", path = null;

            try
            {
                foreach (var kv in documentList.GetConsumingEnumerable())
                {
                    var document = kv.Value;

                    lastKey = kv.Key;

                    // Set the evaluation context
                    CustomContext context = new CustomContext {
                        ["key"] = lastKey
                    };

                    XPathExpression xpath = pathExpression.Clone();
                    xpath.SetContext(context);

                    // Evaluate the path
                    path = document.CreateNavigator().Evaluate(xpath).ToString();

                    if (basePath != null)
                    {
                        path = Path.Combine(basePath, path);
                    }

                    string targetDirectory = Path.GetDirectoryName(path);

                    if (!Directory.Exists(targetDirectory))
                    {
                        Directory.CreateDirectory(targetDirectory);
                    }

                    if (writeXhtmlNamespace)
                    {
                        document.DocumentElement.SetAttribute("xmlns", "http://www.w3.org/1999/xhtml");
                        document.LoadXml(document.OuterXml);
                    }

                    // selectExpression determines which nodes get saved. If there is no selectExpression we simply
                    // save the root node as before. If there is a selectExpression, we evaluate the XPath expression
                    // and save the resulting node set. The select expression also enables the "literal-text" processing
                    // instruction, which outputs its content as unescaped text.
                    if (selectExpression == null)
                    {
                        using (XmlWriter writer = XmlWriter.Create(path, settings))
                        {
                            document.Save(writer);
                        }
                    }
                    else
                    {
                        // IMPLEMENTATION NOTE: The separate StreamWriter is used to maintain XML indenting.  Without it
                        // the XmlWriter won't honor our indent settings after plain text nodes have been written.
                        using (StreamWriter output = File.CreateText(path))
                        {
                            using (XmlWriter writer = XmlWriter.Create(output, settings))
                            {
                                XPathExpression selectXPath = selectExpression.Clone();
                                selectXPath.SetContext(context);

                                XPathNodeIterator ni = document.CreateNavigator().Select(selectExpression);

                                while (ni.MoveNext())
                                {
                                    if (ni.Current.NodeType == XPathNodeType.ProcessingInstruction &&
                                        ni.Current.Name.Equals("literal-text", StringComparison.Ordinal))
                                    {
                                        writer.Flush();
                                        output.Write(ni.Current.Value);
                                    }
                                    else
                                    {
                                        ni.Current.WriteSubtree(writer);
                                    }
                                }
                            }
                        }
                    }

                    // Raise an event to indicate that a file was created
                    this.OnComponentEvent(new FileCreatedEventArgs(this.GroupId, "Save Component", kv.Key, path, true));
                }
            }
            catch (IOException e)
            {
                documentList.CompleteAdding();
                this.WriteMessage(lastKey, MessageLevel.Error, "An access error occurred while attempting to " +
                                  "save to the file '{0}'. The error message is '{1}'", path, e.GetExceptionMessage());
            }
            catch (XmlException e)
            {
                documentList.CompleteAdding();
                this.WriteMessage(lastKey, MessageLevel.Error, "Invalid XML was written to the output " +
                                  "file '{0}'. The error message is '{1}'", path, e.GetExceptionMessage());
            }
            catch (Exception e)
            {
                documentList.CompleteAdding();
                this.WriteMessage(lastKey, MessageLevel.Error, "An error occurred trying to save the topic " +
                                  "content. The error message is '{0}'", e.GetExceptionMessage());
            }
        }