PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/System.Security/Mono.Xml/XmlCanonicalizer.cs

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