XmlTreeViewFindTarget.cs
上传用户:hbhltzc
上传日期:2022-06-04
资源大小:1925k
文件大小:15k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. using System.Xml;
  6. using System.Xml.XPath;
  7. using System.Drawing;
  8. using System.Windows.Forms;
  9. using System.Text.RegularExpressions;
  10. using Microsoft.Xml;
  11. using System.Diagnostics;
  12. namespace XmlNotepad {
  13.     
  14.     internal class XmlTreeViewFindTarget : IFindTarget {
  15.         XmlTreeView view;
  16.         string expression;
  17.         FindFlags flags;
  18.         SearchFilter filter;
  19.         XmlNamespaceManager nsmgr;
  20.         XmlDocument doc;
  21.         ArrayList list;
  22.         XmlTreeNode current;
  23.         Regex regex;
  24.         StringComparison comp;
  25.         XmlNodeMatch match;
  26.         IEditableView ev;
  27.         int start;
  28.         public XmlTreeViewFindTarget(XmlTreeView view) {
  29.             this.view = view;
  30.         }
  31.         bool Backwards { get { return (flags & FindFlags.Backwards) != 0; } }
  32.         bool IsXPath { get { return (flags & FindFlags.XPath) != 0; } }
  33.         bool IsRegex { get { return (flags & FindFlags.Regex) != 0; } }
  34.         bool MatchCase { get { return ((this.flags & FindFlags.MatchCase) != 0); } }
  35.         bool WholeWord { get { return (this.flags & FindFlags.WholeWord) != 0; } }
  36.         void FindNodes() {
  37.             XmlDocument doc = this.view.Model.Document;
  38.             if (this.doc != doc) {
  39.                 this.doc = doc;
  40.                 this.nsmgr = new XmlNamespaceManager(doc.NameTable);
  41.             }
  42.             this.view.Model.ModelChanged += new EventHandler<ModelChangedEventArgs>(OnModelChanged);
  43.             
  44.             string expr = "//node()";
  45.             if (filter == SearchFilter.Comments) {
  46.                 expr = "//comment()";
  47.             }
  48.             this.regex = null;
  49.             if (IsXPath) {
  50.                 expr = this.expression;                
  51.             } else if (IsRegex) {
  52.                 this.regex = new Regex(this.expression);                
  53.             }
  54.             this.comp = MatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase;
  55.             list = new ArrayList();
  56.             foreach (XmlNode node in this.doc.SelectNodes(expr, this.nsmgr)) {
  57.                 MatchNode(node);
  58.                 XmlElement e = node as XmlElement;
  59.                 if (!IsXPath && e != null && e.HasAttributes) {
  60.                     foreach (XmlAttribute a in e.Attributes) {
  61.                         MatchNode(a);
  62.                     }
  63.                 }
  64.             }
  65.         }
  66.         private void MatchNode(XmlNode node) {
  67.             if (!IsXPath) {
  68.                 if (filter == SearchFilter.Comments && node.NodeType == XmlNodeType.Comment) {
  69.                     MatchStrings(list, node, node.Value, false);
  70.                 } else {
  71.                     bool namedNode = (node.NodeType == XmlNodeType.Element ||
  72.                         node.NodeType == XmlNodeType.Attribute ||
  73.                         node.NodeType == XmlNodeType.EntityReference ||
  74.                         node.NodeType == XmlNodeType.ProcessingInstruction ||
  75.                         node.NodeType == XmlNodeType.XmlDeclaration);
  76.                     if ((filter == SearchFilter.Names && namedNode) || filter == SearchFilter.Everything) {
  77.                         MatchStrings(list, node, node.Name, true);
  78.                     }
  79.                     if (filter == SearchFilter.Text || filter == SearchFilter.Everything) {
  80.                         MatchStrings(list, node, node.Value, false);
  81.                     }
  82.                 }
  83.             } else {
  84.                 bool name = (node is XmlElement || node is XmlAttribute);
  85.                 int len = name ? node.Name.Length : node.Value.Length;
  86.                 list.Add(new XmlNodeMatch(node, 0, len, name));
  87.             }
  88.         }
  89.         void OnModelChanged(object sender, ModelChangedEventArgs e) {
  90.             // Then the list of matching nodes we found might now be invalid!
  91.             this.list = null;
  92.         }
  93.         object[] a = new object[1];
  94.         char[] ws = new char[] { ' ', 't', 'n', 'r', '.', ',', ';', '!', ''', '"', '+', '=', '-', '<', '>', '(', ')' };
  95.         void MatchStrings(ArrayList list, XmlNode node, string value, bool name) {
  96.             if (string.IsNullOrEmpty(value)) 
  97.                 return;
  98.             // Normalize the newlines the same way the text editor does so that we
  99.             // don't get off-by-one errors after newlines.
  100.             if (value.IndexOf('n') >= 0 && value.IndexOf("rn") < 0) {
  101.                 value = value.Replace("n", "rn");
  102.             }
  103.             a[0] = value;
  104.             object[] strings = a;
  105.             if (value.IndexOfAny(ws) >= 0 && WholeWord) {
  106.                 strings = value.Split(ws, StringSplitOptions.RemoveEmptyEntries);
  107.             }
  108.             int len = this.expression.Length;
  109.             int index = 0;
  110.             foreach (string word in strings) {
  111.                 index = value.IndexOf(word, index);
  112.                 if (this.regex != null) {
  113.                     foreach (Match m in this.regex.Matches(word)){
  114.                         list.Add(new XmlNodeMatch(node, m.Index + index, m.Length, name));
  115.                     }
  116.                 } else if (this.WholeWord) {
  117.                     if (string.Compare(this.expression, word, comp) == 0) {
  118.                         list.Add(new XmlNodeMatch(node, index, len, name));
  119.                     }
  120.                 } else {
  121.                     int j = word.IndexOf(this.expression, 0, comp);
  122.                     while (j >= 0) {
  123.                         list.Add(new XmlNodeMatch(node, j+index, len, name));
  124.                         j = word.IndexOf(this.expression, j+len, comp);
  125.                     } 
  126.                 }
  127.             }
  128.         }
  129.         void CheckCurrentState(string expression, FindFlags flags, SearchFilter filter) {
  130.             if (this.expression != expression || this.flags != flags || this.filter != filter) {
  131.                 this.expression = expression;
  132.                 this.flags = flags;
  133.                 this.filter = filter;
  134.                 this.list = null;
  135.                 this.match = null;
  136.             }
  137.         }
  138.         #region IFindTarget Members
  139.         public FindResult FindNext(string expression, FindFlags flags, SearchFilter filter) {
  140.             CheckCurrentState(expression, flags, filter);
  141.             if (ev != null && ev.IsEditing) {
  142.                 start = ev.SelectionStart; // remember where we were in the editor.
  143.                 ev.EndEdit(false);
  144.             }
  145.             this.ev = null;
  146.             this.match = null;
  147.             if (string.IsNullOrEmpty(expression))
  148.                 return FindResult.None;
  149.             if (this.list == null) {
  150.                 FindNodes();
  151.             }
  152.             
  153.             // In case user changed the selection since the last find.
  154.             int pos = FindSelectedNode();
  155.             int wrap = -1;
  156.             bool first = true;
  157.             bool hasSomething = this.list.Count > 0;
  158.             while (this.list != null && hasSomething && 
  159.                 (first || pos != wrap)) {
  160.                 first = false;
  161.                 if (wrap == -1) wrap = pos;
  162.                 if (this.Backwards) {
  163.                     pos--;
  164.                     if (pos < 0) pos = list.Count - 1;
  165.                 } else {
  166.                     pos++;
  167.                     if (pos >= list.Count) pos = 0;
  168.                 }
  169.                 XmlNodeMatch m = list[pos] as XmlNodeMatch;
  170.                 XmlNode node = m.Node;
  171.                 this.match = m;
  172.                 if (node != null) {
  173.                     this.current = this.view.FindNode(node);
  174.                     if (this.current == this.view.SelectedNode) {
  175.                         continue;
  176.                     }
  177.                     if (this.current != null) {
  178.                         this.view.SelectedNode = this.current;
  179.                         if (m.IsName) {
  180.                             ev = this.view.TreeView;
  181.                         } else {
  182.                             ev = this.view.NodeTextView;
  183.                         }
  184.                         if (ev.BeginEdit(null)) {
  185.                             ev.SelectText(m.Index, m.Length);
  186.                         }
  187.                     }
  188.                 }
  189.                 return FindResult.Found;
  190.             }
  191.             return hasSomething ? FindResult.NoMore : FindResult.None;
  192.         }
  193.         int FindSelectedNode() {
  194.             // Now find where the selectedNode is in the matching list so we can start there.
  195.             int pos = -1;
  196.             XmlNode selectedNode = null;
  197.             XmlTreeNode selected = this.view.SelectedNode;
  198.             if (selected != null && list != null) {
  199.                 selectedNode = selected.Node;
  200.                 if (selectedNode != null) {
  201.                     // I'm not using XPathNavigator.ComparePosition because it is returning XmlNodeOrder.Unknown
  202.                     // sometimes which is not very useful!
  203.                     foreach (XmlNodeMatch m in list) {
  204.                         XmlNode node = m.Node;
  205.                         if (node == selectedNode) {
  206.                             if (m.Index >= start)
  207.                                 return ++pos; // selected node is one of the matching nodes.
  208.                         } else if (IsNodeAfter(selectedNode, node)) {
  209.                             break;
  210.                         }
  211.                         pos++;
  212.                     }
  213.                 } 
  214.             }
  215.             if (Backwards) pos++;
  216.             return pos;
  217.         }
  218.         // returns true if the match node comes after the selected node in document order.
  219.         bool IsNodeAfter(XmlNode selected, XmlNode match) {
  220.             if (FindCommonParent(ref selected, ref match)) {
  221.                 XmlNode next = selected.NextSibling;
  222.                 while (next != null) {
  223.                     if (next == match) return true;
  224.                     next = next.NextSibling;
  225.                 }
  226.             }
  227.             return false;
  228.         }
  229.         bool FindCommonParent(ref XmlNode a, ref XmlNode b) {
  230.             List<XmlNode> aparents = GetParentChain(a);
  231.             List<XmlNode> bparents = GetParentChain(b);
  232.             // now find the lowest common node.
  233.             for (int i = aparents.Count - 1; i >= 0; i--) {
  234.                 XmlNode p1 = aparents[i];
  235.                 for (int j = bparents.Count - 1; j >= 0; j--) {
  236.                     XmlNode p2 = bparents[j];
  237.                     if (p1 == p2) {
  238.                         // Ok, found the common parent, so now return the
  239.                         // siblings under this parent so we can calculate 
  240.                         // relative document order of those siblings.
  241.                         if (i + 1 < aparents.Count)
  242.                             a = aparents[i + 1];
  243.                         if (j + 1 < bparents.Count)
  244.                             b = bparents[j + 1];
  245.                         return true;
  246.                     }
  247.                 }
  248.             }
  249.             return false;
  250.         }
  251.         List<XmlNode> GetParentChain(XmlNode p) {
  252.             List<XmlNode> parents = new List<XmlNode>();
  253.             p = p.SelectSingleNode("..");
  254.             while (p != null) {
  255.                 parents.Insert(0, p);
  256.                 p = p.ParentNode;
  257.             }
  258.             return parents;
  259.         }
  260.         public Rectangle MatchRect {
  261.             get {
  262.                 if (current != null && match != null) {
  263.                     Rectangle bounds;
  264.                     if (match.IsName) {
  265.                         bounds = view.TreeView.EditorBounds;
  266.                     } else {
  267.                         bounds = view.NodeTextView.EditorBounds;
  268.                     }
  269.                     return bounds;
  270.                 }
  271.                 return Rectangle.Empty;
  272.             }
  273.         }
  274.         public bool ReplaceCurrent( string replaceWith) {
  275.             if (this.ev != null && this.ev.IsEditing && this.match != null) {
  276.                 this.list.Remove(this.match);
  277.                 this.ev.ReplaceText(this.match.Index, this.match.Length, replaceWith);
  278.                 return true;
  279.             }
  280.             return false;
  281.         }
  282.         public string Location {
  283.             get {
  284.                 return this.GetLocation();
  285.             }
  286.         }
  287.         public XmlNamespaceManager Namespaces {
  288.             get {
  289.                 if (nsmgr == null) {
  290.                     this.GetLocation();
  291.                 }
  292.                 return nsmgr;
  293.             }
  294.             set {
  295.                 this.nsmgr = value;
  296.             }
  297.         }
  298.         
  299.         #endregion
  300.         private string GetLocation() {
  301.             string path = null;
  302.             this.doc = this.view.Model.Document;
  303.             this.nsmgr = new XmlNamespaceManager(doc.NameTable);
  304.             XmlTreeNode node = this.view.SelectedNode as XmlTreeNode;
  305.             if (node != null) {
  306.                 XmlNode xnode = node.Node;
  307.                 if (xnode != null) {
  308.                     XmlNode nsctx = xnode;
  309.                     if (nsctx.NodeType != XmlNodeType.Element) {
  310.                         if (nsctx.NodeType == XmlNodeType.Attribute) {
  311.                             nsctx = ((XmlAttribute)nsctx).OwnerElement;
  312.                         } else {
  313.                             nsctx = nsctx.ParentNode;
  314.                         }
  315.                     }
  316.                     if (nsctx == null || nsctx == this.doc)
  317.                         nsctx = this.doc.DocumentElement;
  318.                     foreach (XmlAttribute a in nsctx.SelectNodes("namespace::*")) {
  319.                         string prefix = (a.Prefix == "xmlns") ? a.LocalName : "";
  320.                         if (!string.IsNullOrEmpty(prefix)) {
  321.                             string ns = a.Value;
  322.                             if (nsmgr.LookupPrefix(ns) == null) {
  323.                                 nsmgr.AddNamespace(prefix, ns);
  324.                             }
  325.                         }
  326.                     }
  327.                     foreach (XmlElement child in nsctx.SelectNodes("//*[namespace-uri(.) != '']")) {
  328.                         string uri = child.NamespaceURI;
  329.                         if (nsmgr.LookupPrefix(uri) == null) {
  330.                             string prefix = child.Prefix;
  331.                             if (!string.IsNullOrEmpty(prefix)) {
  332.                                 nsmgr.AddNamespace(prefix, uri);
  333.                             }
  334.                         }
  335.                     }
  336.                     XPathGenerator gen = new XPathGenerator();
  337.                     path = gen.GetXPath(xnode, nsmgr);
  338.                 }
  339.             }
  340.             return path;
  341.         }
  342.         class XmlNodeMatch {
  343.             int index;
  344.             int length;
  345.             XmlNode node;
  346.             bool name;
  347.             public XmlNodeMatch(XmlNode node, int index, int length, bool isName) {
  348.                 this.index = index;
  349.                 this.node = node;
  350.                 this.length = length;
  351.                 this.name = isName;
  352.             }
  353.             public XmlNode Node { get { return this.node; } }
  354.             public int Index { get { return this.index; } }
  355.             public int Length { get { return this.length; } }
  356.             public bool IsName { get { return this.name; } }
  357.         }
  358.     }
  359. }