[runtime] Remove handler block trampoline code
[mono.git] / mcs / class / System.Security / Mono.Xml / XmlCanonicalizer.cs
1 //
2 // XmlCanonicalizer.cs - C14N implementation for XML Signature
3 // http://www.w3.org/TR/xml-c14n
4 //
5 // Author:
6 //      Aleksey Sanin (aleksey@aleksey.com)
7 //
8 // (C) 2003 Aleksey Sanin (aleksey@aleksey.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.Collections;
33 using System.IO;
34 using System.Text;
35 using System.Xml;
36
37 namespace Mono.Xml { 
38
39         internal class XmlCanonicalizer {
40
41                 private enum XmlCanonicalizerState
42                 {
43                         BeforeDocElement,
44                         InsideDocElement,
45                         AfterDocElement
46                 }
47                 
48                 // c14n parameters
49                 private bool comments;
50                 private bool exclusive;
51                 string inclusiveNamespacesPrefixList;
52
53                 // input/output
54                 private XmlNodeList xnl;
55                 private StringBuilder res;
56                 
57                 // namespaces rendering stack
58                 private XmlCanonicalizerState state;
59                 private ArrayList visibleNamespaces;
60                 private int prevVisibleNamespacesStart;
61                 private int prevVisibleNamespacesEnd;
62                 private Hashtable propagatedNss;
63
64                 public XmlCanonicalizer (bool withComments, bool excC14N, Hashtable propagatedNamespaces)
65                 {           
66                         res = new StringBuilder ();
67                         comments = withComments;
68                         exclusive = excC14N;
69                         propagatedNss = propagatedNamespaces;
70                 }
71                 
72                 void Initialize ()
73                 {
74                         state = XmlCanonicalizerState.BeforeDocElement;
75                         visibleNamespaces = new ArrayList ();
76                         prevVisibleNamespacesStart = 0;
77                         prevVisibleNamespacesEnd = 0;
78                         res.Length = 0;
79                 }
80                 
81                 public Stream Canonicalize (XmlDocument doc)
82                 {
83                         if (doc == null)
84                                 throw new ArgumentNullException ("doc");
85                         Initialize ();
86                         
87                         FillMissingPrefixes (doc, new XmlNamespaceManager (doc.NameTable), new ArrayList ());
88                         WriteDocumentNode (doc);
89                         
90                         UTF8Encoding utf8 = new UTF8Encoding ();
91                         byte[] data = utf8.GetBytes (res.ToString ());
92                         return new MemoryStream (data);
93                 }
94                 
95                 public Stream Canonicalize (XmlNodeList nodes)
96                 {
97                         xnl = nodes;
98                         if (nodes == null || nodes.Count < 1)
99                                 return new MemoryStream ();
100                         XmlNode n = nodes [0];
101                         return Canonicalize (n.NodeType == XmlNodeType.Document ? n as XmlDocument : n.OwnerDocument);
102                 }               
103
104                 // See xml-enc-c14n specification
105                 public string InclusiveNamespacesPrefixList {
106                         get { return inclusiveNamespacesPrefixList; }
107                         set { inclusiveNamespacesPrefixList = value; }
108                 }
109
110                 XmlAttribute CreateXmlns (XmlNode n)
111                 {
112                         XmlAttribute a = n.Prefix.Length == 0 ?
113                                 n.OwnerDocument.CreateAttribute ("xmlns", "http://www.w3.org/2000/xmlns/") :
114                                 n.OwnerDocument.CreateAttribute ("xmlns", n.Prefix, "http://www.w3.org/2000/xmlns/");
115                         a.Value = n.NamespaceURI;
116                         return a;
117                 }
118
119                 // Note that this must be done *before* filtering nodes out
120                 // by context node list.
121                 private void FillMissingPrefixes (XmlNode n, XmlNamespaceManager nsmgr, ArrayList tmpList)
122                 {
123                         if (n.Prefix.Length == 0 && propagatedNss != null) {
124                                 foreach (DictionaryEntry de in propagatedNss)
125                                         if ((string) de.Value == n.NamespaceURI) {
126                                                 n.Prefix = (string) de.Key;
127                                                 break;
128                                         }
129                         }
130                         
131                         if (n.NodeType == XmlNodeType.Element && ((XmlElement) n).HasAttributes) {
132                                 foreach (XmlAttribute a in n.Attributes)
133                                         if (a.NamespaceURI == "http://www.w3.org/2000/xmlns/")
134                                                 nsmgr.AddNamespace (a.Prefix.Length == 0 ? String.Empty : a.LocalName, a.Value);
135                                 nsmgr.PushScope ();
136                         }
137
138                         if (n.NamespaceURI.Length > 0 && nsmgr.LookupPrefix (n.NamespaceURI) == null)
139                                 tmpList.Add (CreateXmlns (n));
140
141                         if (n.NodeType == XmlNodeType.Element && ((XmlElement) n).HasAttributes) {
142                                 foreach (XmlAttribute a in n.Attributes)
143                                         if (a.NamespaceURI.Length > 0 && nsmgr.LookupNamespace (a.Prefix) == null)
144                                                 tmpList.Add (CreateXmlns (a));
145                         }
146
147                         foreach (XmlAttribute a in tmpList)
148                                 ((XmlElement) n).SetAttributeNode (a);
149                         tmpList.Clear ();
150
151                         if (n.HasChildNodes) {
152                                 for (XmlNode c = n.FirstChild; c != null; c = c.NextSibling)
153                                         if (c.NodeType == XmlNodeType.Element)
154                                                 FillMissingPrefixes (c, nsmgr, tmpList);
155                         }
156                         nsmgr.PopScope ();
157                 }
158
159                 private void WriteNode (XmlNode node)
160                 {
161                         // Console.WriteLine ("C14N Debug: node=" + node.Name);
162
163                         bool visible = IsNodeVisible (node);
164                         switch (node.NodeType) {
165                         case XmlNodeType.Document:
166                         case XmlNodeType.DocumentFragment:
167                                 WriteDocumentNode (node);
168                                 break;
169                         case XmlNodeType.Element:
170                                 WriteElementNode (node, visible);
171                                 break;
172                         case XmlNodeType.CDATA:
173                         case XmlNodeType.SignificantWhitespace:
174                         case XmlNodeType.Text:
175                                 // CDATA sections are processed as text nodes
176                                 WriteTextNode (node, visible);
177                                 break;
178                         case XmlNodeType.Whitespace:
179                                 if (state == XmlCanonicalizerState.InsideDocElement)
180                                         WriteTextNode (node, visible);
181                                 break;
182                         case XmlNodeType.Comment:
183                                 WriteCommentNode (node, visible);
184                                 break;
185                         case XmlNodeType.ProcessingInstruction:
186                                 WriteProcessingInstructionNode (node, visible);
187                                 break;
188                         case XmlNodeType.EntityReference:
189                                 for (int i = 0; i < node.ChildNodes.Count; i++)
190                                         WriteNode (node.ChildNodes [i]);
191                                 break;
192                         case XmlNodeType.Attribute:
193                                 throw new XmlException ("Attribute node is impossible here", null);
194                         case XmlNodeType.EndElement:
195                                 throw new XmlException ("EndElement node is impossible here", null);
196                         case XmlNodeType.EndEntity:
197                                 throw new XmlException ("EndEntity node is impossible here", null);
198                         case XmlNodeType.DocumentType:
199                         case XmlNodeType.Entity:
200                         case XmlNodeType.Notation:
201                         case XmlNodeType.XmlDeclaration:
202                                 // just do nothing
203                                 break;
204                         }
205                 }
206
207                 private void WriteDocumentNode (XmlNode node)
208                 {
209                         state = XmlCanonicalizerState.BeforeDocElement;
210                         for (XmlNode child = node.FirstChild; child != null; child = child.NextSibling)
211                                 WriteNode (child);
212                 }
213                 
214                 // Element Nodes
215                 // If the element is not in the node-set, then the result is obtained 
216                 // by processing the namespace axis, then the attribute axis, then 
217                 // processing the child nodes of the element that are in the node-set 
218                 // (in document order). If the element is inthe node-set, then the result 
219                 // is an open angle bracket (<), the element QName, the result of 
220                 // processing the namespace axis, the result of processing the attribute 
221                 // axis, a close angle bracket (>), the result of processing the child 
222                 // nodes of the element that are in the node-set (in document order), an 
223                 // open angle bracket, a forward slash (/), the element QName, and a close 
224                 // angle bracket.
225                 private void WriteElementNode (XmlNode node, bool visible)
226                 {
227                         // Console.WriteLine ("Debug: element node");
228                     
229                         // remember current state 
230                         int savedPrevVisibleNamespacesStart = prevVisibleNamespacesStart;
231                         int savedPrevVisibleNamespacesEnd = prevVisibleNamespacesEnd;
232                         int savedVisibleNamespacesSize = visibleNamespaces.Count;
233                         XmlCanonicalizerState s = state;
234                         if (visible && state == XmlCanonicalizerState.BeforeDocElement)
235                                 state = XmlCanonicalizerState.InsideDocElement;
236                     
237                         // write start tag
238                         if (visible) {
239                                 res.Append ("<");
240                                 res.Append (node.Name);
241                         }
242                     
243                         // this is odd but you can select namespaces
244                         // and attributes even if node itself is not visible
245                         WriteNamespacesAxis (node, visible);
246                         WriteAttributesAxis (node);                     
247         
248                         if (visible)
249                                 res.Append (">");
250
251                         // write children
252                         for (XmlNode child = node.FirstChild; child != null; child = child.NextSibling)
253                                 WriteNode (child);
254                                     
255                         // write end tag            
256                         if (visible) {
257                                 res.Append ("</");
258                                 res.Append (node.Name);
259                                 res.Append (">");
260                         }
261                     
262                         // restore state
263                         if (visible && s == XmlCanonicalizerState.BeforeDocElement)
264                                 state = XmlCanonicalizerState.AfterDocElement;
265                         prevVisibleNamespacesStart = savedPrevVisibleNamespacesStart;
266                         prevVisibleNamespacesEnd = savedPrevVisibleNamespacesEnd;
267                         if (visibleNamespaces.Count > savedVisibleNamespacesSize) {
268                                 visibleNamespaces.RemoveRange (savedVisibleNamespacesSize, 
269                                         visibleNamespaces.Count - savedVisibleNamespacesSize);
270                         }
271                 }
272
273                 // Namespace Axis
274                 // Consider a list L containing only namespace nodes in the 
275                 // axis and in the node-set in lexicographic order (ascending). To begin 
276                 // processing L, if the first node is not the default namespace node (a node 
277                 // with no namespace URI and no local name), then generate a space followed 
278                 // by xmlns="" if and only if the following conditions are met:
279                 //    - the element E that owns the axis is in the node-set
280                 //    - The nearest ancestor element of E in the node-set has a default 
281                 //          namespace node in the node-set (default namespace nodes always 
282                 //      have non-empty values in XPath)
283                 // The latter condition eliminates unnecessary occurrences of xmlns="" in 
284                 // the canonical form since an element only receives an xmlns="" if its 
285                 // default namespace is empty and if it has an immediate parent in the 
286                 // canonical form that has a non-empty default namespace. To finish 
287                 // processing  L, simply process every namespace node in L, except omit 
288                 // namespace node with local name xml, which defines the xml prefix, 
289                 // if its string value is http://www.w3.org/XML/1998/namespace.
290                 private void WriteNamespacesAxis (XmlNode node, bool visible)
291                 {
292                         // Console.WriteLine ("Debug: namespaces");
293
294                         XmlDocument doc = node.OwnerDocument;    
295                         bool has_empty_namespace = false;
296                         ArrayList list = new ArrayList ();
297                         for (XmlNode cur = node; cur != null && cur != doc; cur = cur.ParentNode) {
298                                 foreach (XmlAttribute attribute in cur.Attributes) {            
299                                         if (!IsNamespaceNode (attribute)) 
300                                                 continue;
301                                 
302                                         // get namespace prefix
303                                         string prefix = string.Empty;
304                                         if (attribute.Prefix == "xmlns") 
305                                                 prefix = attribute.LocalName;
306                             
307                                         // check if it is "xml" namespace                           
308                                         if (prefix == "xml" && attribute.Value == "http://www.w3.org/XML/1998/namespace")
309                                                 continue;
310                             
311                                         // make sure that this is an active namespace
312                                         // for our node
313                                         string ns = node.GetNamespaceOfPrefix (prefix);
314                                         if (ns != attribute.Value) 
315                                                 continue;
316                             
317                                         // check that it is selected with XPath
318                                         if (!IsNodeVisible (attribute)) 
319                                                 continue;
320
321                                         // check that we have not rendered it yet
322                                         bool rendered = IsNamespaceRendered (prefix, attribute.Value);
323
324                                         // For exc-c14n, only visibly utilized
325                                         // namespaces are written.
326                                         if (exclusive && !IsVisiblyUtilized (node as XmlElement, attribute))
327                                                 continue;
328
329                                         // add to the visible namespaces stack
330                                         if (visible)
331                                                 visibleNamespaces.Add (attribute);                            
332                             
333                                         if (!rendered)
334                                                 list.Add (attribute);
335                                     
336                                         if (prefix == string.Empty)
337                                                 has_empty_namespace = true;
338                                 }
339                         }
340
341                         // add empty namespace if needed
342                         if (visible && !has_empty_namespace && !IsNamespaceRendered (string.Empty, string.Empty) && node.NamespaceURI == String.Empty)
343                                 res.Append (" xmlns=\"\"");
344                     
345                         list.Sort (new XmlDsigC14NTransformNamespacesComparer ());
346                         foreach (object obj in list) {
347                                 XmlNode attribute = (obj as XmlNode);
348                                 if (attribute != null) {
349                                         res.Append (" ");
350                                         res.Append (attribute.Name);
351                                         res.Append ("=\"");
352                                         res.Append (attribute.Value);
353                                         res.Append ("\"");
354                                 }
355                         }
356                     
357                         // move the rendered namespaces stack
358                         if (visible) {
359                                 prevVisibleNamespacesStart = prevVisibleNamespacesEnd;
360                                 prevVisibleNamespacesEnd = visibleNamespaces.Count;     
361                         }
362                 }
363                 
364                 // Attribute Axis 
365                 // In lexicographic order (ascending), process each node that 
366                 // is in the element's attribute axis and in the node-set.
367                 // 
368                 // The processing of an element node E MUST be modified slightly 
369                 // when an XPath node-set is given as input and the element's 
370                 // parent is omitted from the node-set.
371                 private void WriteAttributesAxis (XmlNode node)
372                 {
373                         // Console.WriteLine ("Debug: attributes");
374                 
375                         ArrayList list = new ArrayList ();
376                         foreach (XmlNode attribute in node.Attributes) {        
377                                 if (!IsNamespaceNode (attribute) && IsNodeVisible (attribute))
378                                         list.Add (attribute);
379                         }
380                      
381                         // Add attributes from "xml" namespace for "inclusive" c14n only:
382                         //
383                         // The method for processing the attribute axis of an element E 
384                         // in the node-set is enhanced. All element nodes along E's 
385                         // ancestor axis are examined for nearest occurrences of 
386                         // attributes in the xml namespace, such as xml:lang and 
387                         // xml:space (whether or not they are in the node-set). 
388                         // From this list of attributes, remove any that are in E's 
389                         // attribute axis (whether or not they are in the node-set). 
390                         // Then, lexicographically merge this attribute list with the 
391                         // nodes of E's attribute axis that are in the node-set. The 
392                         // result of visiting the attribute axis is computed by 
393                         // processing the attribute nodes in this merged attribute list.
394                         if (!exclusive && node.ParentNode != null && node.ParentNode.ParentNode != null && !IsNodeVisible (node.ParentNode.ParentNode)) {
395                                 // if we have whole document then the node.ParentNode.ParentNode
396                                 // is always visible
397                                 for (XmlNode cur = node.ParentNode; cur != null; cur = cur.ParentNode) {
398                                         if (cur.Attributes == null)
399                                                 continue;
400                                         foreach (XmlNode attribute in cur.Attributes) {
401                                                 // we are looking for "xml:*" attributes
402                                                 if (attribute.Prefix != "xml")
403                                                         continue;
404                                 
405                                                 // exclude ones that are in the node's attributes axis
406                                                 if (node.Attributes.GetNamedItem (attribute.LocalName, attribute.NamespaceURI) != null)
407                                                         continue;
408                                 
409                                                 // finally check that we don't have the same attribute in our list
410                                                 bool found = false;
411                                                 foreach (object obj in list) {
412                                                         XmlNode n = (obj as XmlNode);
413                                                         if (n.Prefix == "xml" && n.LocalName == attribute.LocalName) {
414                                                                 found = true;
415                                                                 break;
416                                                         }
417                                                 }
418                                 
419                                                 if (found) 
420                                                         continue;
421                                 
422                                                 // now we can add this attribute to our list
423                                                 list.Add (attribute);
424                                         }
425                                 }               
426                         }
427                         
428                         // sort namespaces and write results        
429                         list.Sort (new XmlDsigC14NTransformAttributesComparer ());
430                         foreach (object obj in list) {
431                                 XmlNode attribute = (obj as XmlNode);
432                                 if (attribute != null) {
433                                         res.Append (" ");
434                                         res.Append (attribute.Name);
435                                         res.Append ("=\"");
436                                         res.Append (NormalizeString (attribute.Value, XmlNodeType.Attribute));
437                                         res.Append ("\"");
438                                 }
439                         }
440                 }
441
442                 // Text Nodes
443                 // the string value, except all ampersands are replaced 
444                 // by &amp;, all open angle brackets (<) are replaced by &lt;, all closing 
445                 // angle brackets (>) are replaced by &gt;, and all #xD characters are 
446                 // replaced by &#xD;.
447                 private void WriteTextNode (XmlNode node, bool visible)
448                 {
449                         // Console.WriteLine ("Debug: text node");
450                         if (visible)
451                                 res.Append (NormalizeString (node.Value, node.NodeType));
452 //                              res.Append (NormalizeString (node.Value, XmlNodeType.Text));
453                 }               
454
455                 // Comment Nodes
456                 // Nothing if generating canonical XML without comments. For 
457                 // canonical XML with comments, generate the opening comment 
458                 // symbol (<!--), the string value of the node, and the 
459                 // closing comment symbol (-->). Also, a trailing #xA is rendered 
460                 // after the closing comment symbol for comment children of the 
461                 // root node with a lesser document order than the document 
462                 // element, and a leading #xA is rendered before the opening 
463                 // comment symbol of comment children of the root node with a 
464                 // greater document order than the document element. (Comment 
465                 // children of the root node represent comments outside of the 
466                 // top-level document element and outside of the document type 
467                 // declaration).
468                 private void WriteCommentNode (XmlNode node, bool visible)
469                 {
470                         // Console.WriteLine ("Debug: comment node");
471                         if (visible && comments) {
472                             if (state == XmlCanonicalizerState.AfterDocElement)
473                                     res.Append ("\x0A<!--");
474                             else
475                                     res.Append ("<!--");
476                         
477                             res.Append (NormalizeString (node.Value, XmlNodeType.Comment));
478                             
479                             if (state == XmlCanonicalizerState.BeforeDocElement)
480                                     res.Append ("-->\x0A");
481                             else
482                                     res.Append ("-->");
483                         }
484                 }
485                 
486                 // Processing Instruction (PI) Nodes- 
487                 // The opening PI symbol (<?), the PI target name of the node, 
488                 // a leading space and the string value if it is not empty, and 
489                 // the closing PI symbol (?>). If the string value is empty, 
490                 // then the leading space is not added. Also, a trailing #xA is 
491                 // rendered after the closing PI symbol for PI children of the 
492                 // root node with a lesser document order than the document 
493                 // element, and a leading #xA is rendered before the opening PI 
494                 // symbol of PI children of the root node with a greater document 
495                 // order than the document element.
496                 private void WriteProcessingInstructionNode (XmlNode node, bool visible)
497                 {
498                         // Console.WriteLine ("Debug: PI node");
499
500                         if (visible) {
501                                 if (state == XmlCanonicalizerState.AfterDocElement)
502                                         res.Append ("\x0A<?");
503                                 else
504                                         res.Append ("<?");
505                         
506                                 res.Append (node.Name);
507                                 if (node.Value.Length > 0) {
508                                         res.Append (" ");
509                                         res.Append (NormalizeString (node.Value, XmlNodeType.ProcessingInstruction));
510                                 }
511                         
512                                 if (state == XmlCanonicalizerState.BeforeDocElement)
513                                         res.Append ("?>\x0A");
514                                 else
515                                         res.Append ("?>");
516                         }
517                 }
518
519                 // determines whether the node is in the node-set or not.
520                 private bool IsNodeVisible (XmlNode node)
521                 {
522                         // if node list is empty then we process whole document
523                         if (xnl == null) 
524                                 return true;
525                     
526                         // walk thru the list
527                         foreach (XmlNode xn in xnl) {
528                                 if (node.Equals (xn)) 
529                                         return true;
530                         }
531                     
532                         return false;
533                 }
534                 
535                 // This method assumes that the namespace node is *not*
536                 // rendered yet.
537                 private bool IsVisiblyUtilized (XmlElement owner, XmlAttribute ns)
538                 {
539                         if (owner == null)
540                                 return false;
541
542                         string prefix = ns.LocalName == "xmlns" ? String.Empty : ns.LocalName;
543                         if (owner.Prefix == prefix && owner.NamespaceURI == ns.Value)
544                                 return true;
545                         if (!owner.HasAttributes)
546                                 return false;
547                         foreach (XmlAttribute a in owner.Attributes) {
548                                 if (a.Prefix == String.Empty)
549                                         continue;
550                                 if (a.Prefix != prefix || a.NamespaceURI != ns.Value)
551                                         continue;
552                                 if (IsNodeVisible (a))
553                                         return true;
554                         }
555                         return false;
556                 }
557
558                 private bool IsNamespaceRendered (string prefix, string uri)
559                 {
560                         // if the default namespace xmlns="" is not re-defined yet
561                         // then we do not want to print it out
562                         bool IsEmptyNs = prefix == string.Empty && uri == string.Empty;
563                         int start = (IsEmptyNs) ? 0 : prevVisibleNamespacesStart;
564                         for (int i = visibleNamespaces.Count - 1; i >= start; i--) {
565                                 XmlNode node = (visibleNamespaces[i] as XmlNode);
566                                 if (node != null) {
567                                         // get namespace prefix
568                                         string p = string.Empty;
569                                         if (node.Prefix == "xmlns") 
570                                                 p = node.LocalName;
571                                         if (p == prefix)
572                                                 return node.Value == uri;
573                                 }
574                         }
575                     
576                         return IsEmptyNs;
577                 }
578                 
579                 private bool IsNamespaceNode (XmlNode node)
580                 {
581                         if (node == null || node.NodeType != XmlNodeType.Attribute) 
582                                 return false;
583                         return node.NamespaceURI == "http://www.w3.org/2000/xmlns/";
584                 }
585     
586                 private bool IsTextNode (XmlNodeType type)
587                 {
588                         switch (type) {
589                         case XmlNodeType.Text:
590                         case XmlNodeType.CDATA:
591                         case XmlNodeType.SignificantWhitespace:
592                         case XmlNodeType.Whitespace:
593                                 return true;
594                         }
595                         return false;
596                 }
597
598                 private string NormalizeString (string input, XmlNodeType type)
599                 {
600                         StringBuilder sb = new StringBuilder ();
601                         for (int i = 0; i < input.Length; i++) {
602                                 char ch = input[i];
603                                 if (ch == '<' && (type == XmlNodeType.Attribute || IsTextNode (type)))
604                                         sb.Append ("&lt;");
605                                 else if (ch == '>' && IsTextNode (type))
606                                         sb.Append ("&gt;");
607                                 else if (ch == '&' && (type == XmlNodeType.Attribute || IsTextNode (type)))
608                                         sb.Append ("&amp;");
609                                 else if (ch == '\"' && type == XmlNodeType.Attribute)
610                                         sb.Append ("&quot;");
611                                 else if (ch == '\x09' && type == XmlNodeType.Attribute)
612                                         sb.Append ("&#x9;");
613                                 else if (ch == '\x0A' && type == XmlNodeType.Attribute)
614                                         sb.Append ("&#xA;");
615                                 else if (ch == '\x0D')
616                                         sb.Append ("&#xD;");
617                                 else
618                                         sb.Append (ch);
619                         }
620                     
621                         return sb.ToString ();
622                 }
623         }
624     
625         internal class XmlDsigC14NTransformAttributesComparer : IComparer
626         {
627                 public int Compare (object x, object y)
628                 {
629                         XmlNode n1 = (x as XmlNode);
630                         XmlNode n2 = (y as XmlNode);
631                 
632                         // simple cases
633                         if (n1 == n2) 
634                                 return 0;
635                         else if (n1 == null) 
636                                 return -1;
637                         else if (n2 == null) 
638                                 return 1;
639                         else if (n1.Prefix == n2.Prefix) 
640                                 return string.CompareOrdinal (n1.LocalName, n2.LocalName);
641         
642                         // Attributes in the default namespace are first
643                         // because the default namespace is not applied to
644                         // unqualified attributes
645                         if (n1.Prefix == string.Empty) 
646                                 return -1;
647                         else if (n2.Prefix == string.Empty) 
648                                 return 1;
649                     
650                         int ret = string.Compare (n1.NamespaceURI, n2.NamespaceURI);
651                         if (ret == 0)
652                                 ret = string.Compare (n1.LocalName, n2.LocalName);
653                         return ret;
654                 }
655         }
656
657         internal class XmlDsigC14NTransformNamespacesComparer : IComparer
658         {
659                 public int Compare (object x, object y)
660                 {
661                         XmlNode n1 = (x as XmlNode);
662                         XmlNode n2 = (y as XmlNode);
663                 
664                         // simple cases
665                         if (n1 == n2) 
666                                 return 0;
667                         else if (n1 == null) 
668                                 return -1;
669                         else if (n2 == null) 
670                                 return 1;
671                         else if (n1.Prefix == string.Empty) 
672                                 return -1;
673                         else if (n2.Prefix == string.Empty) 
674                                 return 1;
675                     
676                         return string.Compare (n1.LocalName, n2.LocalName);
677                 }
678         }
679 }
680