summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/XmlCat/Program.cs
blob: 7b0ab64852d267875280836f6827e195bdd758dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright © Microsoft Corporation.
// This source file is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using Microsoft.Ddue.Tools.CommandLine;

namespace MergeXml
{
    class Program
    {
        static int Main(string[] args)
        {
            ConsoleApplication.WriteBanner();

            // get and validate args
            OptionCollection programOptions = new OptionCollection();
            programOptions.Add(new SwitchOption("?", "Show this help page."));
            programOptions.Add(new StringOption("out", "(String) Path to the file that the input files should be merged in to. Required."));
            programOptions.Add(new StringOption("position", "(String) The name of the element or elements to which the input elements will be appended. Required."));
            programOptions.Add(new StringOption("include", @"(String) An xpath expression indicating which elements from the source files should be introduced in to the output file. The default is '/'"));
            ParseArgumentsResult options = programOptions.ParseArguments(args);
            if (options.Options["?"].IsPresent || !options.Options["out"].IsPresent || !options.Options["position"].IsPresent)
            {
                programOptions.WriteOptionSummary(Console.Error);
                Console.WriteLine();
                Console.WriteLine("file1 file2 ...");
                Console.WriteLine("The input files to operate on.");
                return 0;
            }
            // ensure output file exists
            string outputPath = options.Options["out"].Value.ToString();
            if (!File.Exists(outputPath))
            {
                ConsoleApplication.WriteMessage(LogLevel.Error, "The specified output file, which the input files are to be merged in to, doesn't exist.");
                return 1;
            }

            // ensure a postition element name was passed
            if (String.IsNullOrEmpty(options.Options["position"].Value.ToString()))
            {
                ConsoleApplication.WriteMessage(LogLevel.Error, "No position element name was provided.");
                return 1;
            }
            string positionName = options.Options["position"].Value.ToString();

            // validate xpaths ("include" switch)
            string xpath;
            if (options.Options["include"].IsPresent)
                xpath = options.Options["include"].Value.ToString();
            else
                xpath = @"/";
            XPathExpression includeExpression;
            try
            {
                includeExpression = XPathExpression.Compile(xpath);
            }
            catch (XPathException)
            {
                ConsoleApplication.WriteMessage(LogLevel.Error, "The xpath expression provided by the include switch, '" + xpath + "', is invalid.");
                return 1;
            }

            // get list of input files to operate on
            if (options.UnusedArguments.Count == 0)
            {
                ConsoleApplication.WriteMessage(LogLevel.Error, "No input files were provided.");
                return 1;
            }
            string[] inputFiles = new string[options.UnusedArguments.Count];
            options.UnusedArguments.CopyTo(inputFiles, 0);

            // ensure all input files exist
            foreach (string path in inputFiles)
            {
                if (!File.Exists(path))
                {
                    ConsoleApplication.WriteMessage(LogLevel.Error, "Specified input file '" + path + "' doesn't exist.");
                    return 1;
                }
            }


            // open the output file and move to the position
            XmlWriterSettings outputSettings = new XmlWriterSettings();
            outputSettings.Indent = true;
            outputSettings.Encoding = Encoding.UTF8;
            using (XmlWriter output = XmlWriter.Create(outputPath + ".tmp", outputSettings))
            {
                // start printing output doc string until the selected node is matched
                using (XmlReader source = XmlReader.Create(outputPath))
                {
                    while (!source.EOF)
                    {
                        source.Read();

                        switch (source.NodeType)
                        {
                            case XmlNodeType.Element:
                                output.WriteStartElement(source.Prefix, source.LocalName, source.NamespaceURI);
                                output.WriteAttributes(source, true);
                                if (source.IsEmptyElement)
                                {
                                    output.WriteEndElement();
                                }
                                if (String.Equals(source.Name, positionName, StringComparison.OrdinalIgnoreCase))
                                {
                                    // start introducing the elements from the input files
                                    foreach (string path in inputFiles)
                                    {
                                        XPathDocument inputDoc = new XPathDocument(path);
                                        XPathNavigator inputNav = inputDoc.CreateNavigator();
                                        XPathNodeIterator inputNodesIterator = inputNav.Select(includeExpression);
                                        while (inputNodesIterator.MoveNext())
                                        {
                                            output.WriteNode(inputNodesIterator.Current, true);
                                        }
                                    }
                                }
                                break;
                            case XmlNodeType.Text:
                                output.WriteString(source.Value);
                                break;
                            case XmlNodeType.Whitespace:
                            case XmlNodeType.SignificantWhitespace:
                                output.WriteWhitespace(source.Value);
                                break;
                            case XmlNodeType.CDATA:
                                output.WriteCData(source.Value);
                                break;
                            case XmlNodeType.EntityReference:
                                output.WriteEntityRef(source.Name);
                                break;
                            case XmlNodeType.XmlDeclaration:
                            case XmlNodeType.ProcessingInstruction:
                                output.WriteProcessingInstruction(source.Name, source.Value);
                                break;
                            case XmlNodeType.DocumentType:
                                output.WriteDocType(source.Name, source.GetAttribute("PUBLIC"), source.GetAttribute("SYSTEM"), source.Value);
                                break;
                            case XmlNodeType.Comment:
                                output.WriteComment(source.Value);
                                break;
                            case XmlNodeType.EndElement:
                                output.WriteFullEndElement();
                                break;
                        }
                    }
                }
                output.WriteEndDocument();
                output.Close();
            }

            File.Delete(outputPath);
            File.Move(outputPath + ".tmp", outputPath);

            return 0; // pau
        }
    }
}