PageRenderTime 1204ms CodeModel.GetById 38ms RepoModel.GetById 1ms app.codeStats 0ms

/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/SegmentTree.cs

http://github.com/mono/monodevelop
C# | 444 lines | 356 code | 57 blank | 31 comment | 95 complexity | 48af239c21ba84806725c9fbd5f56835 MD5 | raw file
Possible License(s): LGPL-2.0, GPL-2.0, CC-BY-SA-3.0, MIT, LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. //
  2. // SegmentTree.cs
  3. //
  4. // Author:
  5. // mkrueger <mkrueger@novell.com>
  6. //
  7. // Copyright (c) 2011 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. using System;
  27. using System.Collections.Generic;
  28. using System.Linq;
  29. using Mono.TextEditor.Utils;
  30. namespace Mono.TextEditor
  31. {
  32. /// <summary>
  33. /// A segment tree contains overlapping segments and get all segments overlapping a segment. It's implemented as a augmented interval tree
  34. /// described in Cormen et al. (2001, Section 14.3: Interval trees, pp. 311–317).
  35. /// </summary>
  36. public class SegmentTree<T> : TextSegmentTree where T : TreeSegment
  37. {
  38. internal readonly RedBlackTree<T> tree = new RedBlackTree<T> ();
  39. public int Count {
  40. get {
  41. return tree.Count;
  42. }
  43. }
  44. public bool IsDirty {
  45. get;
  46. set;
  47. }
  48. public IEnumerable<T> Segments {
  49. get {
  50. var root = tree.Root;
  51. if (root == null)
  52. yield break;
  53. var node = root.GetOuterLeft ();
  54. while (node != null) {
  55. yield return node;
  56. node = node.GetNextNode ();
  57. }
  58. }
  59. }
  60. TextDocument ownerDocument;
  61. public void InstallListener (TextDocument doc)
  62. {
  63. if (ownerDocument != null)
  64. throw new InvalidOperationException ("Segment tree already installed");
  65. ownerDocument = doc;
  66. doc.TextReplaced += UpdateOnTextReplace;
  67. }
  68. public void RemoveListener ()
  69. {
  70. if (ownerDocument == null)
  71. throw new InvalidOperationException ("Segment tree is not installed");
  72. ownerDocument.TextReplaced -= UpdateOnTextReplace;
  73. ownerDocument = null;
  74. }
  75. public void Clear ()
  76. {
  77. IsDirty = false;
  78. tree.Clear ();
  79. }
  80. public void UpdateOnTextReplace (object sender, DocumentChangeEventArgs e)
  81. {
  82. IsDirty = true;
  83. if (e.RemovalLength == 0) {
  84. var length = e.InsertionLength;
  85. foreach (var segment in GetSegmentsAt (e.Offset).Where (s => s.Offset < e.Offset && e.Offset < s.EndOffset)) {
  86. segment.Length += length;
  87. segment.UpdateAugmentedData ();
  88. }
  89. var node = SearchFirstSegmentWithStartAfter (e.Offset);
  90. if (node != null) {
  91. node.DistanceToPrevNode += length;
  92. node.UpdateAugmentedData ();
  93. }
  94. return;
  95. }
  96. int delta = e.ChangeDelta;
  97. foreach (var segment in new List<T> (GetSegmentsOverlapping (e.Offset, e.RemovalLength))) {
  98. if (segment.Offset < e.Offset) {
  99. if (segment.EndOffset >= e.Offset + e.RemovalLength) {
  100. segment.Length += delta;
  101. } else {
  102. segment.Length = e.Offset - segment.Offset;
  103. }
  104. segment.UpdateAugmentedData ();
  105. continue;
  106. }
  107. int remainingLength = segment.EndOffset - (e.Offset + e.RemovalLength);
  108. Remove (segment);
  109. if (remainingLength > 0) {
  110. segment.Offset = e.Offset + e.RemovalLength;
  111. segment.Length = remainingLength;
  112. Add (segment);
  113. }
  114. }
  115. var next = SearchFirstSegmentWithStartAfter (e.Offset + 1);
  116. if (next != null) {
  117. next.DistanceToPrevNode += delta;
  118. next.UpdateAugmentedData ();
  119. }
  120. }
  121. public void Add (TreeSegment node)
  122. {
  123. if (node == null)
  124. throw new ArgumentNullException ("node");
  125. if (node.segmentTree != null)
  126. throw new InvalidOperationException ("Node already attached.");
  127. node.segmentTree = this;
  128. int insertionOffset = node.Offset;
  129. node.DistanceToMaxEnd = node.Length;
  130. if (tree.Root == null) {
  131. tree.Count = 1;
  132. tree.Root = (T)node;
  133. node.TotalLength = node.DistanceToPrevNode;
  134. return;
  135. }
  136. if (insertionOffset < tree.Root.TotalLength) {
  137. var n = SearchNode (ref insertionOffset);
  138. node.TotalLength = node.DistanceToPrevNode = insertionOffset;
  139. n.DistanceToPrevNode -= insertionOffset;
  140. tree.InsertBefore (n, node);
  141. return;
  142. }
  143. node.DistanceToPrevNode = node.TotalLength = insertionOffset - tree.Root.TotalLength;
  144. tree.InsertRight (tree.Root.GetOuterRight (), node);
  145. }
  146. public bool Remove (TreeSegment node)
  147. {
  148. if (node.segmentTree == null)
  149. return false;
  150. if (node.segmentTree != this)
  151. throw new InvalidOperationException ("Tried to remove tree segment from wrong tree.");
  152. var calculatedOffset = node.Offset;
  153. var next = node.GetNextNode ();
  154. if (next != null)
  155. next.DistanceToPrevNode += node.DistanceToPrevNode;
  156. tree.Remove (node);
  157. if (next != null)
  158. next.UpdateAugmentedData ();
  159. node.segmentTree = null;
  160. node.parent = node.left = node.right = null;
  161. node.DistanceToPrevNode = calculatedOffset;
  162. return true;
  163. }
  164. TreeSegment SearchFirstSegmentWithStartAfter (int startOffset)
  165. {
  166. if (tree.Root == null)
  167. return null;
  168. if (startOffset <= 0)
  169. return tree.Root.GetOuterLeft ();
  170. var result = SearchNode (ref startOffset);
  171. while (startOffset == 0) {
  172. var pre = result == null ? tree.Root.GetOuterRight () : result.GetPrevNode ();
  173. if (pre == null)
  174. return null;
  175. startOffset += pre.DistanceToPrevNode;
  176. result = pre;
  177. }
  178. return result;
  179. }
  180. TreeSegment SearchNode (ref int offset)
  181. {
  182. TreeSegment n = tree.Root;
  183. while (true) {
  184. if (n.left != null) {
  185. if (offset < n.left.TotalLength) {
  186. n = n.left;
  187. continue;
  188. }
  189. offset -= n.left.TotalLength;
  190. }
  191. if (offset < n.DistanceToPrevNode)
  192. return n;
  193. offset -= n.DistanceToPrevNode;
  194. if (n.right == null)
  195. return null;
  196. n = n.right;
  197. }
  198. }
  199. public IEnumerable<T> GetSegmentsAt (int offset)
  200. {
  201. return GetSegmentsOverlapping (offset, 0);
  202. }
  203. public IEnumerable<T> GetSegmentsOverlapping (TextSegment segment)
  204. {
  205. if (segment.IsInvalid)
  206. return Enumerable.Empty<T> ();
  207. return GetSegmentsOverlapping (segment.Offset, segment.Length);
  208. }
  209. struct Interval
  210. {
  211. internal TreeSegment node;
  212. internal int start, end;
  213. public Interval (TreeSegment node,int start,int end)
  214. {
  215. this.node = node;
  216. this.start = start;
  217. this.end = end;
  218. }
  219. public override string ToString ()
  220. {
  221. return string.Format ("[Interval: start={0},end={1}]", start, end);
  222. }
  223. }
  224. public IEnumerable<T> GetSegmentsOverlapping (int offset, int length)
  225. {
  226. if (tree.Root == null)
  227. yield break;
  228. Stack<Interval> intervalStack = new Stack<Interval> ();
  229. intervalStack.Push (new Interval (tree.Root, offset, offset + length));
  230. while (intervalStack.Count > 0) {
  231. var interval = intervalStack.Pop ();
  232. if (interval.end < 0)
  233. continue;
  234. var node = interval.node;
  235. int nodeStart = interval.start - node.DistanceToPrevNode;
  236. int nodeEnd = interval.end - node.DistanceToPrevNode;
  237. var leftNode = node.left;
  238. if (leftNode != null) {
  239. nodeStart -= leftNode.TotalLength;
  240. nodeEnd -= leftNode.TotalLength;
  241. }
  242. if (node.DistanceToMaxEnd < nodeStart)
  243. continue;
  244. if (leftNode != null)
  245. intervalStack.Push (new Interval (leftNode, interval.start, interval.end));
  246. if (nodeEnd < 0)
  247. continue;
  248. if (nodeStart <= node.Length)
  249. yield return (T)node;
  250. var rightNode = node.right;
  251. if (rightNode != null)
  252. intervalStack.Push (new Interval (rightNode, nodeStart, nodeEnd));
  253. }
  254. }
  255. }
  256. interface TextSegmentTree
  257. {
  258. void Add (TreeSegment segment);
  259. bool Remove (TreeSegment segment);
  260. }
  261. public class TreeSegment : IRedBlackTreeNode
  262. {
  263. internal TextSegmentTree segmentTree;
  264. public int Offset {
  265. get {
  266. if (segmentTree == null)
  267. return DistanceToPrevNode;
  268. var curNode = this;
  269. int offset = curNode.DistanceToPrevNode;
  270. if (curNode.left != null)
  271. offset += curNode.left.TotalLength;
  272. while (curNode.parent != null) {
  273. if (curNode == curNode.parent.right) {
  274. if (curNode.parent.left != null)
  275. offset += curNode.parent.left.TotalLength;
  276. offset += curNode.parent.DistanceToPrevNode;
  277. }
  278. curNode = curNode.parent;
  279. }
  280. return offset;
  281. }
  282. set {
  283. if (segmentTree != null)
  284. segmentTree.Remove (this);
  285. DistanceToPrevNode = value;
  286. if (segmentTree != null)
  287. segmentTree.Add (this);
  288. }
  289. }
  290. public int Length {
  291. get;
  292. set;
  293. }
  294. public int EndOffset {
  295. get {
  296. return Offset + Length;
  297. }
  298. }
  299. public TextSegment Segment {
  300. get {
  301. return new TextSegment (Offset, Length);
  302. }
  303. }
  304. // TotalLength = DistanceToPrevNode + Left.DistanceToPrevNode + Right.DistanceToPrevNode
  305. internal int TotalLength;
  306. internal int DistanceToPrevNode;
  307. // DistanceToMaxEnd = Max (Length, left.DistanceToMaxEnd + Max (left.Offset, right.Offset) - Offset)
  308. internal int DistanceToMaxEnd;
  309. protected TreeSegment ()
  310. {
  311. }
  312. public TreeSegment (int offset, int length)
  313. {
  314. Offset = offset;
  315. Length = length;
  316. }
  317. public TreeSegment (TextSegment segment) : this (segment.Offset, segment.Length)
  318. {
  319. }
  320. public bool Contains (int offset)
  321. {
  322. return Offset <= offset && offset < EndOffset;
  323. }
  324. public bool Contains (TextSegment segment)
  325. {
  326. return Offset <= segment.Offset && segment.EndOffset <= EndOffset;
  327. }
  328. #region IRedBlackTreeNode implementation
  329. public void UpdateAugmentedData ()
  330. {
  331. int totalLength = DistanceToPrevNode;
  332. int distanceToMaxEnd = Length;
  333. if (left != null) {
  334. totalLength += left.TotalLength;
  335. int leftdistance = left.DistanceToMaxEnd - DistanceToPrevNode;
  336. if (left.right != null)
  337. leftdistance -= left.right.TotalLength;
  338. if (leftdistance > distanceToMaxEnd)
  339. distanceToMaxEnd = leftdistance;
  340. }
  341. if (right != null) {
  342. totalLength += right.TotalLength;
  343. int rightdistance = right.DistanceToMaxEnd + right.DistanceToPrevNode;
  344. if (right.left != null)
  345. rightdistance += right.left.TotalLength;
  346. if (rightdistance > distanceToMaxEnd)
  347. distanceToMaxEnd = rightdistance;
  348. }
  349. if (TotalLength != totalLength || DistanceToMaxEnd != distanceToMaxEnd) {
  350. TotalLength = totalLength;
  351. DistanceToMaxEnd = distanceToMaxEnd;
  352. if (parent != null)
  353. parent.UpdateAugmentedData ();
  354. }
  355. }
  356. internal TreeSegment parent, left, right;
  357. Mono.TextEditor.Utils.IRedBlackTreeNode Mono.TextEditor.Utils.IRedBlackTreeNode.Parent {
  358. get {
  359. return parent;
  360. }
  361. set {
  362. parent = (TreeSegment)value;
  363. }
  364. }
  365. Mono.TextEditor.Utils.IRedBlackTreeNode Mono.TextEditor.Utils.IRedBlackTreeNode.Left {
  366. get {
  367. return left;
  368. }
  369. set {
  370. left = (TreeSegment)value;
  371. }
  372. }
  373. IRedBlackTreeNode Mono.TextEditor.Utils.IRedBlackTreeNode.Right {
  374. get {
  375. return right;
  376. }
  377. set {
  378. right = (TreeSegment)value;
  379. }
  380. }
  381. RedBlackColor Mono.TextEditor.Utils.IRedBlackTreeNode.Color {
  382. get;
  383. set;
  384. }
  385. #endregion
  386. }
  387. }