4 using System.Xml.XPath;
\r
5 using System.Collections;
\r
6 using System.Reflection;
\r
7 using System.ComponentModel;
\r
10 namespace XmlNormalizer {
\r
12 /// Summary description for Class1.
\r
14 class XmlNormalizer {
\r
15 class OptionLetterAttribute:Attribute{
\r
17 public OptionLetterAttribute(char c):base(){
\r
20 public override string ToString() {
\r
21 return _c.ToString();
\r
26 bool _removeWhiteSpace;
\r
27 bool _sortAttributes;
\r
28 bool _removeAttributes;
\r
29 bool _removeNamespacesAndPrefixes;
\r
35 [Description("remove white space")]
\r
36 public bool RemoveWhiteSpace {
\r
37 get {return _removeWhiteSpace;}
\r
38 set {_removeWhiteSpace=value;}
\r
41 [Description("sort attributes")]
\r
42 public bool SortAttributes {
\r
43 get {return _sortAttributes;}
\r
44 set {_sortAttributes=value;}
\r
47 [Description("remove attributes")]
\r
48 public bool RemoveAttributes {
\r
49 get {return _removeAttributes;}
\r
50 set {_removeAttributes=value;}
\r
53 [Description("remove namespaces and prefixes")]
\r
54 public bool RemoveNamespacesAndPrefixes {
\r
55 get {return _removeNamespacesAndPrefixes;}
\r
56 set {_removeNamespacesAndPrefixes=value;}
\r
59 [Description("remove text nodes")]
\r
60 public bool RemoveText {
\r
61 get {return _removeText;}
\r
62 set {_removeText=value;}
\r
65 [Description("remove all except element nodes")]
\r
66 public bool RemoveAll {
\r
67 get {return _removeAll;}
\r
68 set {_removeAll=value;}
\r
71 [Description("insert newlines before elements")]
\r
72 public bool NewLines {
\r
73 get {return _newLines;}
\r
74 set {_newLines=value;}
\r
77 [Description("minimal normalizing")]
\r
78 public bool MinimalNormalizing {
\r
82 public XmlNormalizer ()
\r
86 public XmlNormalizer (string options) {
\r
87 ParseOptions(options);
\r
90 public void Process(TextReader rd) {
\r
91 doc=new XmlDocument();
\r
92 doc.PreserveWhitespace = true;
\r
94 string fileContents = rd.ReadToEnd();
\r
97 doc.LoadXml (fileContents);
\r
99 catch (Exception x) {
\r
100 StringBuilder sb = new StringBuilder ();
\r
101 sb.Append ("<NormalizerRoot>");
\r
102 sb.Append (fileContents);
\r
103 sb.Append ("</NormalizerRoot>");
\r
104 doc.LoadXml (sb.ToString ());
\r
109 RemoveWhiteSpace = true;
\r
112 RemoveNamespacesAndPrefixes = true;
\r
114 XmlDocument newDoc = new XmlDocument();
\r
116 CopyNodes(newDoc, doc, newDoc);
\r
121 void CopyNodes (XmlDocument newDoc, XmlNode fromParent, XmlNode toParent) {
\r
122 if (fromParent.HasChildNodes)
\r
123 foreach (XmlNode c in fromParent.ChildNodes)
\r
124 CopyNode (newDoc, c, toParent);
\r
126 if (fromParent.Attributes != null) {
\r
127 string [] keys = new string [fromParent.Attributes.Count];
\r
129 for (int i=0; i<fromParent.Attributes.Count; i++) {
\r
130 keys[i] = fromParent.Attributes[i].Name;
\r
132 if (SortAttributes){
\r
135 for (int i=0; i<keys.Length; i++) {
\r
136 CopyNode (newDoc, fromParent.Attributes[keys[i]], toParent);
\r
141 void CopyNode (XmlDocument newDoc, XmlNode from, XmlNode toParent) {
\r
142 if (RemoveAll && from.NodeType != XmlNodeType.Element)
\r
145 XmlNode child = null;
\r
146 bool newLineNode = false;
\r
148 switch (from.NodeType) {
\r
149 case XmlNodeType.Element:
\r
150 newLineNode = true;
\r
151 if (RemoveNamespacesAndPrefixes)
\r
152 child = newDoc.CreateElement (from.LocalName);
\r
154 XmlElement e = from as XmlElement;
\r
155 child = newDoc.CreateElement (e.Prefix, e.LocalName, e.NamespaceURI);
\r
158 case XmlNodeType.Attribute: {
\r
159 if (RemoveAttributes)
\r
162 XmlAttribute fromAttr = from as XmlAttribute;
\r
163 if (!fromAttr.Specified)
\r
168 if (RemoveNamespacesAndPrefixes)
\r
169 a = newDoc.CreateAttribute (fromAttr.LocalName);
\r
171 a = newDoc.CreateAttribute (fromAttr.Prefix, fromAttr.LocalName, fromAttr.NamespaceURI);
\r
173 toParent.Attributes.Append(a);
\r
174 CopyNodes (newDoc, from, a);
\r
177 case XmlNodeType.CDATA:
\r
178 newLineNode = true;
\r
179 child = newDoc.CreateCDataSection ((from as XmlCDataSection).Data);
\r
181 case XmlNodeType.Comment:
\r
182 if (RemoveWhiteSpace)
\r
184 newLineNode = true;
\r
185 child = newDoc.CreateComment ((from as XmlComment).Data);
\r
187 case XmlNodeType.ProcessingInstruction:
\r
188 newLineNode = true;
\r
189 XmlProcessingInstruction pi = from as XmlProcessingInstruction;
\r
190 child = newDoc.CreateProcessingInstruction (pi.Target, pi.Data);
\r
192 case XmlNodeType.DocumentType:
\r
193 newLineNode = true;
\r
194 toParent.AppendChild (from.CloneNode (true));
\r
196 case XmlNodeType.EntityReference:
\r
197 child = newDoc.CreateEntityReference ((from as XmlEntityReference).Name);
\r
199 case XmlNodeType.SignificantWhitespace:
\r
200 if (RemoveWhiteSpace)
\r
202 child = newDoc.CreateSignificantWhitespace (from.Value);
\r
204 case XmlNodeType.Text:
\r
207 newLineNode = true;
\r
208 child = newDoc.CreateTextNode (from.Value);
\r
210 case XmlNodeType.Whitespace:
\r
211 if (RemoveWhiteSpace)
\r
213 child = newDoc.CreateWhitespace (from.Value);
\r
215 case XmlNodeType.XmlDeclaration:
\r
216 newLineNode = true;
\r
217 XmlDeclaration d = from as XmlDeclaration;
\r
218 XmlDeclaration d1 = newDoc.CreateXmlDeclaration (d.Version, d.Encoding, d.Standalone);
\r
219 newDoc.InsertBefore(d1, newDoc.DocumentElement);
\r
222 if (NewLines && newLineNode && toParent.NodeType != XmlNodeType.Attribute) {
\r
223 XmlSignificantWhitespace s = newDoc.CreateSignificantWhitespace("\r\n");
\r
224 toParent.AppendChild (s);
\r
226 toParent.AppendChild(child);
\r
227 CopyNodes (newDoc, from, child);
\r
230 public void ParseOptions (string options) {
\r
231 _removeWhiteSpace = false;
\r
232 _sortAttributes = false;
\r
233 _removeAttributes = false;
\r
234 _removeNamespacesAndPrefixes = false;
\r
235 _removeText = false;
\r
236 _removeAll = false;
\r
238 foreach (PropertyInfo pi in typeof (XmlNormalizer).GetProperties()) {
\r
239 string option = pi.GetCustomAttributes(typeof(OptionLetterAttribute),true)[0].ToString();
\r
240 if (options.IndexOf(option) == -1)
\r
242 pi.GetSetMethod().Invoke (this, new object [] {true});
\r
246 public static Hashtable GetOptions() {
\r
247 Hashtable h = new Hashtable();
\r
249 foreach (PropertyInfo pi in typeof (XmlNormalizer).GetProperties()) {
\r
250 string option = pi.GetCustomAttributes(typeof(OptionLetterAttribute),true)[0].ToString();
\r
251 string descr = (pi.GetCustomAttributes(typeof(DescriptionAttribute), true)[0] as DescriptionAttribute).Description;
\r
257 public void Output(XmlWriter wr) {
\r
261 public void Output(TextWriter wr) {
\r
262 Output (new XmlTextWriter (wr));
\r
265 void ProcessFile (string inputfile, string outputfile) {
\r
266 StreamWriter wr = null;
\r
267 StreamReader rd = null;
\r
269 wr = new StreamWriter (outputfile);
\r
270 rd = new StreamReader (inputfile);
\r
271 ProcessFile (rd, wr);
\r
272 } catch (Exception) {
\r
279 File.Copy (inputfile, outputfile, true);
\r
288 void ProcessFile (TextReader input, TextWriter output) {
\r
289 XmlTextWriter xwr = new XmlTextWriter (output);
\r
295 void ProcessDirectory (string inputdir, string outputdir) {
\r
296 if (!Directory.Exists (outputdir))
\r
297 Directory.CreateDirectory (outputdir);
\r
298 DirectoryInfo idi = new DirectoryInfo(inputdir);
\r
299 foreach (FileInfo fi in idi.GetFiles()) {
\r
300 string outputfile = Path.Combine(outputdir, fi.Name);
\r
301 ProcessFile (fi.FullName, outputfile);
\r
303 foreach (DirectoryInfo di in idi.GetDirectories())
\r
304 ProcessDirectory (di.FullName, Path.Combine(outputdir, di.Name));
\r
306 #if !XML_NORMALIZER_NO_MAIN
\r
308 /// The main entry point for the application.
\r
311 static int Main(string[] args) {
\r
312 if (args.Length < 2 || args[0].Length < 2 || args[0][0] != '-') {
\r
316 XmlNormalizer norm = new XmlNormalizer (args[0].Substring(1));
\r
317 if (File.Exists(args[1])) {
\r
318 if (args.Length != 2) {
\r
322 norm.ProcessFile(new StreamReader (args[1]), Console.Out);
\r
324 else if (Directory.Exists (args[1])) {
\r
325 if (args.Length != 3) {
\r
329 norm.ProcessDirectory (args[1], args[2]);
\r
332 Console.Error.WriteLine("Path not found: {0}", args[1]);
\r
337 static void PrintUsage () {
\r
338 Console.Error.WriteLine("Usage: xmlnorm -<flags> <inputfile>");
\r
339 Console.Error.WriteLine("Or: xmlnorm -<flags> <inputdir> <outputdir>");
\r
340 Console.Error.WriteLine("\tFlags:");
\r
341 foreach (DictionaryEntry de in XmlNormalizer.GetOptions())
\r
342 Console.Error.WriteLine ("\t{0}\t{1}", de.Key, de.Value);
\r