1 | // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team |
---|
2 | // |
---|
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this |
---|
4 | // software and associated documentation files (the "Software"), to deal in the Software |
---|
5 | // without restriction, including without limitation the rights to use, copy, modify, merge, |
---|
6 | // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
---|
7 | // to whom the Software is furnished to do so, subject to the following conditions: |
---|
8 | // |
---|
9 | // The above copyright notice and this permission notice shall be included in all copies or |
---|
10 | // substantial portions of the Software. |
---|
11 | // |
---|
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
---|
13 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
---|
14 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
---|
15 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
---|
16 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
---|
17 | // DEALINGS IN THE SOFTWARE. |
---|
18 | |
---|
19 | using System; |
---|
20 | using System.Globalization; |
---|
21 | using System.IO; |
---|
22 | using System.Text; |
---|
23 | |
---|
24 | namespace ICSharpCode.AvalonEdit.Utils |
---|
25 | { |
---|
26 | /// <summary> |
---|
27 | /// Poor man's template specialization: extension methods for Rope<char>. |
---|
28 | /// </summary> |
---|
29 | public static class CharRope |
---|
30 | { |
---|
31 | /// <summary> |
---|
32 | /// Creates a new rope from the specified text. |
---|
33 | /// </summary> |
---|
34 | public static Rope<char> Create(string text) |
---|
35 | { |
---|
36 | if (text == null) |
---|
37 | throw new ArgumentNullException("text"); |
---|
38 | return new Rope<char>(InitFromString(text)); |
---|
39 | } |
---|
40 | |
---|
41 | /// <summary> |
---|
42 | /// Retrieves the text for a portion of the rope. |
---|
43 | /// Runs in O(lg N + M), where M=<paramref name="length"/>. |
---|
44 | /// </summary> |
---|
45 | /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception> |
---|
46 | /// <remarks> |
---|
47 | /// This method counts as a read access and may be called concurrently to other read accesses. |
---|
48 | /// </remarks> |
---|
49 | public static string ToString(this Rope<char> rope, int startIndex, int length) |
---|
50 | { |
---|
51 | if (rope == null) |
---|
52 | throw new ArgumentNullException("rope"); |
---|
53 | #if DEBUG |
---|
54 | if (length < 0) |
---|
55 | throw new ArgumentOutOfRangeException("length", length, "Value must be >= 0"); |
---|
56 | #endif |
---|
57 | if (length == 0) |
---|
58 | return string.Empty; |
---|
59 | char[] buffer = new char[length]; |
---|
60 | rope.CopyTo(startIndex, buffer, 0, length); |
---|
61 | return new string(buffer); |
---|
62 | } |
---|
63 | |
---|
64 | /// <summary> |
---|
65 | /// Retrieves the text for a portion of the rope and writes it to the specified text writer. |
---|
66 | /// Runs in O(lg N + M), where M=<paramref name="length"/>. |
---|
67 | /// </summary> |
---|
68 | /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception> |
---|
69 | /// <remarks> |
---|
70 | /// This method counts as a read access and may be called concurrently to other read accesses. |
---|
71 | /// </remarks> |
---|
72 | public static void WriteTo(this Rope<char> rope, TextWriter output, int startIndex, int length) |
---|
73 | { |
---|
74 | if (rope == null) |
---|
75 | throw new ArgumentNullException("rope"); |
---|
76 | if (output == null) |
---|
77 | throw new ArgumentNullException("output"); |
---|
78 | rope.VerifyRange(startIndex, length); |
---|
79 | rope.root.WriteTo(startIndex, output, length); |
---|
80 | } |
---|
81 | |
---|
82 | /// <summary> |
---|
83 | /// Appends text to this rope. |
---|
84 | /// Runs in O(lg N + M). |
---|
85 | /// </summary> |
---|
86 | /// <exception cref="ArgumentNullException">newElements is null.</exception> |
---|
87 | public static void AddText(this Rope<char> rope, string text) |
---|
88 | { |
---|
89 | InsertText(rope, rope.Length, text); |
---|
90 | } |
---|
91 | |
---|
92 | /// <summary> |
---|
93 | /// Inserts text into this rope. |
---|
94 | /// Runs in O(lg N + M). |
---|
95 | /// </summary> |
---|
96 | /// <exception cref="ArgumentNullException">newElements is null.</exception> |
---|
97 | /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception> |
---|
98 | public static void InsertText(this Rope<char> rope, int index, string text) |
---|
99 | { |
---|
100 | if (rope == null) |
---|
101 | throw new ArgumentNullException("rope"); |
---|
102 | rope.InsertRange(index, text.ToCharArray(), 0, text.Length); |
---|
103 | /*if (index < 0 || index > rope.Length) { |
---|
104 | throw new ArgumentOutOfRangeException("index", index, "0 <= index <= " + rope.Length.ToString(CultureInfo.InvariantCulture)); |
---|
105 | } |
---|
106 | if (text == null) |
---|
107 | throw new ArgumentNullException("text"); |
---|
108 | if (text.Length == 0) |
---|
109 | return; |
---|
110 | rope.root = rope.root.Insert(index, text); |
---|
111 | rope.OnChanged();*/ |
---|
112 | } |
---|
113 | |
---|
114 | internal static RopeNode<char> InitFromString(string text) |
---|
115 | { |
---|
116 | if (text.Length == 0) { |
---|
117 | return RopeNode<char>.emptyRopeNode; |
---|
118 | } |
---|
119 | RopeNode<char> node = RopeNode<char>.CreateNodes(text.Length); |
---|
120 | FillNode(node, text, 0); |
---|
121 | return node; |
---|
122 | } |
---|
123 | |
---|
124 | static void FillNode(RopeNode<char> node, string text, int start) |
---|
125 | { |
---|
126 | if (node.contents != null) { |
---|
127 | text.CopyTo(start, node.contents, 0, node.length); |
---|
128 | } else { |
---|
129 | FillNode(node.left, text, start); |
---|
130 | FillNode(node.right, text, start + node.left.length); |
---|
131 | } |
---|
132 | } |
---|
133 | |
---|
134 | internal static void WriteTo(this RopeNode<char> node, int index, TextWriter output, int count) |
---|
135 | { |
---|
136 | if (node.height == 0) { |
---|
137 | if (node.contents == null) { |
---|
138 | // function node |
---|
139 | node.GetContentNode().WriteTo(index, output, count); |
---|
140 | } else { |
---|
141 | // leaf node: append data |
---|
142 | output.Write(node.contents, index, count); |
---|
143 | } |
---|
144 | } else { |
---|
145 | // concat node: do recursive calls |
---|
146 | if (index + count <= node.left.length) { |
---|
147 | node.left.WriteTo(index, output, count); |
---|
148 | } else if (index >= node.left.length) { |
---|
149 | node.right.WriteTo(index - node.left.length, output, count); |
---|
150 | } else { |
---|
151 | int amountInLeft = node.left.length - index; |
---|
152 | node.left.WriteTo(index, output, amountInLeft); |
---|
153 | node.right.WriteTo(0, output, count - amountInLeft); |
---|
154 | } |
---|
155 | } |
---|
156 | } |
---|
157 | |
---|
158 | /// <summary> |
---|
159 | /// Gets the index of the first occurrence of any element in the specified array. |
---|
160 | /// </summary> |
---|
161 | /// <param name="rope">The target rope.</param> |
---|
162 | /// <param name="anyOf">Array of characters being searched.</param> |
---|
163 | /// <param name="startIndex">Start index of the search.</param> |
---|
164 | /// <param name="length">Length of the area to search.</param> |
---|
165 | /// <returns>The first index where any character was found; or -1 if no occurrence was found.</returns> |
---|
166 | public static int IndexOfAny(this Rope<char> rope, char[] anyOf, int startIndex, int length) |
---|
167 | { |
---|
168 | if (rope == null) |
---|
169 | throw new ArgumentNullException("rope"); |
---|
170 | if (anyOf == null) |
---|
171 | throw new ArgumentNullException("anyOf"); |
---|
172 | rope.VerifyRange(startIndex, length); |
---|
173 | |
---|
174 | while (length > 0) { |
---|
175 | var entry = rope.FindNodeUsingCache(startIndex).PeekOrDefault(); |
---|
176 | char[] contents = entry.node.contents; |
---|
177 | int startWithinNode = startIndex - entry.nodeStartIndex; |
---|
178 | int nodeLength = Math.Min(entry.node.length, startWithinNode + length); |
---|
179 | for (int i = startIndex - entry.nodeStartIndex; i < nodeLength; i++) { |
---|
180 | char element = contents[i]; |
---|
181 | foreach (char needle in anyOf) { |
---|
182 | if (element == needle) |
---|
183 | return entry.nodeStartIndex + i; |
---|
184 | } |
---|
185 | } |
---|
186 | length -= nodeLength - startWithinNode; |
---|
187 | startIndex = entry.nodeStartIndex + nodeLength; |
---|
188 | } |
---|
189 | return -1; |
---|
190 | } |
---|
191 | |
---|
192 | /// <summary> |
---|
193 | /// Gets the index of the first occurrence of the search text. |
---|
194 | /// </summary> |
---|
195 | public static int IndexOf(this Rope<char> rope, string searchText, int startIndex, int length, StringComparison comparisonType) |
---|
196 | { |
---|
197 | if (rope == null) |
---|
198 | throw new ArgumentNullException("rope"); |
---|
199 | if (searchText == null) |
---|
200 | throw new ArgumentNullException("searchText"); |
---|
201 | rope.VerifyRange(startIndex, length); |
---|
202 | int pos = rope.ToString(startIndex, length).IndexOf(searchText, comparisonType); |
---|
203 | if (pos < 0) |
---|
204 | return -1; |
---|
205 | else |
---|
206 | return pos + startIndex; |
---|
207 | } |
---|
208 | |
---|
209 | /// <summary> |
---|
210 | /// Gets the index of the last occurrence of the search text. |
---|
211 | /// </summary> |
---|
212 | public static int LastIndexOf(this Rope<char> rope, string searchText, int startIndex, int length, StringComparison comparisonType) |
---|
213 | { |
---|
214 | if (rope == null) |
---|
215 | throw new ArgumentNullException("rope"); |
---|
216 | if (searchText == null) |
---|
217 | throw new ArgumentNullException("searchText"); |
---|
218 | rope.VerifyRange(startIndex, length); |
---|
219 | int pos = rope.ToString(startIndex, length).LastIndexOf(searchText, comparisonType); |
---|
220 | if (pos < 0) |
---|
221 | return -1; |
---|
222 | else |
---|
223 | return pos + startIndex; |
---|
224 | } |
---|
225 | } |
---|
226 | } |
---|