Mercurial > projects > dwt-addons
comparison dwtx/jface/text/TextUtilities.d @ 129:eb30df5ca28b
Added JFace Text sources
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 23 Aug 2008 19:10:48 +0200 |
parents | |
children | c4fb132a086c |
comparison
equal
deleted
inserted
replaced
128:8df1d4193877 | 129:eb30df5ca28b |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.text.TextUtilities; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 | |
17 import java.util.HashMap; | |
18 import java.util.HashSet; | |
19 import java.util.Iterator; | |
20 import java.util.List; | |
21 import java.util.ListIterator; | |
22 import java.util.Map; | |
23 import java.util.Set; | |
24 | |
25 import dwtx.core.runtime.Assert; | |
26 | |
27 | |
28 /** | |
29 * A collection of text functions. | |
30 * <p> | |
31 * This class is neither intended to be instantiated nor subclassed. | |
32 * </p> | |
33 * @noinstantiate This class is not intended to be instantiated by clients. | |
34 * @noextend This class is not intended to be subclassed by clients. | |
35 */ | |
36 public class TextUtilities { | |
37 | |
38 /** | |
39 * Default line delimiters used by the text functions of this class. | |
40 */ | |
41 public final static String[] DELIMITERS= new String[] { "\n", "\r", "\r\n" }; //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ | |
42 | |
43 /** | |
44 * Default line delimiters used by these text functions. | |
45 * | |
46 * @deprecated use DELIMITERS instead | |
47 */ | |
48 public final static String[] fgDelimiters= DELIMITERS; | |
49 | |
50 | |
51 | |
52 /** | |
53 * Determines which one of default line delimiters appears first in the list. If none of them the | |
54 * hint is returned. | |
55 * | |
56 * @param text the text to be checked | |
57 * @param hint the line delimiter hint | |
58 * @return the line delimiter | |
59 */ | |
60 public static String determineLineDelimiter(String text, String hint) { | |
61 try { | |
62 int[] info= indexOf(DELIMITERS, text, 0); | |
63 return DELIMITERS[info[1]]; | |
64 } catch (ArrayIndexOutOfBoundsException x) { | |
65 } | |
66 return hint; | |
67 } | |
68 | |
69 /** | |
70 * Returns the starting position and the index of the first matching search string | |
71 * in the given text that is greater than the given offset. If more than one search | |
72 * string matches with the same starting position then the longest one is returned. | |
73 * | |
74 * @param searchStrings the strings to search for | |
75 * @param text the text to be searched | |
76 * @param offset the offset at which to start the search | |
77 * @return an <code>int[]</code> with two elements where the first is the starting offset, the second the index of the found | |
78 * search string in the given <code>searchStrings</code> array, returns <code>[-1, -1]</code> if no match exists | |
79 */ | |
80 public static int[] indexOf(String[] searchStrings, String text, int offset) { | |
81 | |
82 int[] result= { -1, -1 }; | |
83 int zeroIndex= -1; | |
84 | |
85 for (int i= 0; i < searchStrings.length; i++) { | |
86 | |
87 int length= searchStrings[i].length(); | |
88 | |
89 if (length is 0) { | |
90 zeroIndex= i; | |
91 continue; | |
92 } | |
93 | |
94 int index= text.indexOf(searchStrings[i], offset); | |
95 if (index >= 0) { | |
96 | |
97 if (result[0] is -1) { | |
98 result[0]= index; | |
99 result[1]= i; | |
100 } else if (index < result[0]) { | |
101 result[0]= index; | |
102 result[1]= i; | |
103 } else if (index is result[0] && length > searchStrings[result[1]].length()) { | |
104 result[0]= index; | |
105 result[1]= i; | |
106 } | |
107 } | |
108 } | |
109 | |
110 if (zeroIndex > -1 && result[0] is -1) { | |
111 result[0]= 0; | |
112 result[1]= zeroIndex; | |
113 } | |
114 | |
115 return result; | |
116 } | |
117 | |
118 /** | |
119 * Returns the index of the longest search string with which the given text ends or | |
120 * <code>-1</code> if none matches. | |
121 * | |
122 * @param searchStrings the strings to search for | |
123 * @param text the text to search | |
124 * @return the index in <code>searchStrings</code> of the longest string with which <code>text</code> ends or <code>-1</code> | |
125 */ | |
126 public static int endsWith(String[] searchStrings, String text) { | |
127 | |
128 int index= -1; | |
129 | |
130 for (int i= 0; i < searchStrings.length; i++) { | |
131 if (text.endsWith(searchStrings[i])) { | |
132 if (index is -1 || searchStrings[i].length() > searchStrings[index].length()) | |
133 index= i; | |
134 } | |
135 } | |
136 | |
137 return index; | |
138 } | |
139 | |
140 /** | |
141 * Returns the index of the longest search string with which the given text starts or <code>-1</code> | |
142 * if none matches. | |
143 * | |
144 * @param searchStrings the strings to search for | |
145 * @param text the text to search | |
146 * @return the index in <code>searchStrings</code> of the longest string with which <code>text</code> starts or <code>-1</code> | |
147 */ | |
148 public static int startsWith(String[] searchStrings, String text) { | |
149 | |
150 int index= -1; | |
151 | |
152 for (int i= 0; i < searchStrings.length; i++) { | |
153 if (text.startsWith(searchStrings[i])) { | |
154 if (index is -1 || searchStrings[i].length() > searchStrings[index].length()) | |
155 index= i; | |
156 } | |
157 } | |
158 | |
159 return index; | |
160 } | |
161 | |
162 /** | |
163 * Returns the index of the first compare string that equals the given text or <code>-1</code> | |
164 * if none is equal. | |
165 * | |
166 * @param compareStrings the strings to compare with | |
167 * @param text the text to check | |
168 * @return the index of the first equal compare string or <code>-1</code> | |
169 */ | |
170 public static int equals(String[] compareStrings, String text) { | |
171 for (int i= 0; i < compareStrings.length; i++) { | |
172 if (text.equals(compareStrings[i])) | |
173 return i; | |
174 } | |
175 return -1; | |
176 } | |
177 | |
178 /** | |
179 * Returns a document event which is an accumulation of a list of document events, | |
180 * <code>null</code> if the list of documentEvents is empty. | |
181 * The document of the document events are ignored. | |
182 * | |
183 * @param unprocessedDocument the document to which the document events would be applied | |
184 * @param documentEvents the list of document events to merge | |
185 * @return returns the merged document event | |
186 * @throws BadLocationException might be thrown if document is not in the correct state with respect to document events | |
187 */ | |
188 public static DocumentEvent mergeUnprocessedDocumentEvents(IDocument unprocessedDocument, List documentEvents) throws BadLocationException { | |
189 | |
190 if (documentEvents.size() is 0) | |
191 return null; | |
192 | |
193 final Iterator iterator= documentEvents.iterator(); | |
194 final DocumentEvent firstEvent= (DocumentEvent) iterator.next(); | |
195 | |
196 // current merged event | |
197 final IDocument document= unprocessedDocument; | |
198 int offset= firstEvent.getOffset(); | |
199 int length= firstEvent.getLength(); | |
200 final StringBuffer text= new StringBuffer(firstEvent.getText() is null ? "" : firstEvent.getText()); //$NON-NLS-1$ | |
201 | |
202 while (iterator.hasNext()) { | |
203 | |
204 final int delta= text.length() - length; | |
205 | |
206 final DocumentEvent event= (DocumentEvent) iterator.next(); | |
207 final int eventOffset= event.getOffset(); | |
208 final int eventLength= event.getLength(); | |
209 final String eventText= event.getText() is null ? "" : event.getText(); //$NON-NLS-1$ | |
210 | |
211 // event is right from merged event | |
212 if (eventOffset > offset + length + delta) { | |
213 final String string= document.get(offset + length, (eventOffset - delta) - (offset + length)); | |
214 text.append(string); | |
215 text.append(eventText); | |
216 | |
217 length= (eventOffset - delta) + eventLength - offset; | |
218 | |
219 // event is left from merged event | |
220 } else if (eventOffset + eventLength < offset) { | |
221 final String string= document.get(eventOffset + eventLength, offset - (eventOffset + eventLength)); | |
222 text.insert(0, string); | |
223 text.insert(0, eventText); | |
224 | |
225 length= offset + length - eventOffset; | |
226 offset= eventOffset; | |
227 | |
228 // events overlap each other | |
229 } else { | |
230 final int start= Math.max(0, eventOffset - offset); | |
231 final int end= Math.min(text.length(), eventLength + eventOffset - offset); | |
232 text.replace(start, end, eventText); | |
233 | |
234 offset= Math.min(offset, eventOffset); | |
235 final int totalDelta= delta + eventText.length() - eventLength; | |
236 length= text.length() - totalDelta; | |
237 } | |
238 } | |
239 | |
240 return new DocumentEvent(document, offset, length, text.toString()); | |
241 } | |
242 | |
243 /** | |
244 * Returns a document event which is an accumulation of a list of document events, | |
245 * <code>null</code> if the list of document events is empty. | |
246 * The document events being merged must all refer to the same document, to which | |
247 * the document changes have been already applied. | |
248 * | |
249 * @param documentEvents the list of document events to merge | |
250 * @return returns the merged document event | |
251 * @throws BadLocationException might be thrown if document is not in the correct state with respect to document events | |
252 */ | |
253 public static DocumentEvent mergeProcessedDocumentEvents(List documentEvents) throws BadLocationException { | |
254 | |
255 if (documentEvents.size() is 0) | |
256 return null; | |
257 | |
258 final ListIterator iterator= documentEvents.listIterator(documentEvents.size()); | |
259 final DocumentEvent firstEvent= (DocumentEvent) iterator.previous(); | |
260 | |
261 // current merged event | |
262 final IDocument document= firstEvent.getDocument(); | |
263 int offset= firstEvent.getOffset(); | |
264 int length= firstEvent.getLength(); | |
265 int textLength= firstEvent.getText() is null ? 0 : firstEvent.getText().length(); | |
266 | |
267 while (iterator.hasPrevious()) { | |
268 | |
269 final int delta= length - textLength; | |
270 | |
271 final DocumentEvent event= (DocumentEvent) iterator.previous(); | |
272 final int eventOffset= event.getOffset(); | |
273 final int eventLength= event.getLength(); | |
274 final int eventTextLength= event.getText() is null ? 0 : event.getText().length(); | |
275 | |
276 // event is right from merged event | |
277 if (eventOffset > offset + textLength + delta) { | |
278 length= (eventOffset - delta) - (offset + textLength) + length + eventLength; | |
279 textLength= (eventOffset - delta) + eventTextLength - offset; | |
280 | |
281 // event is left from merged event | |
282 } else if (eventOffset + eventTextLength < offset) { | |
283 length= offset - (eventOffset + eventTextLength) + length + eventLength; | |
284 textLength= offset + textLength - eventOffset; | |
285 offset= eventOffset; | |
286 | |
287 // events overlap each other | |
288 } else { | |
289 final int start= Math.max(0, eventOffset - offset); | |
290 final int end= Math.min(length, eventTextLength + eventOffset - offset); | |
291 length += eventLength - (end - start); | |
292 | |
293 offset= Math.min(offset, eventOffset); | |
294 final int totalDelta= delta + eventLength - eventTextLength; | |
295 textLength= length - totalDelta; | |
296 } | |
297 } | |
298 | |
299 final String text= document.get(offset, textLength); | |
300 return new DocumentEvent(document, offset, length, text); | |
301 } | |
302 | |
303 /** | |
304 * Removes all connected document partitioners from the given document and stores them | |
305 * under their partitioning name in a map. This map is returned. After this method has been called | |
306 * the given document is no longer connected to any document partitioner. | |
307 * | |
308 * @param document the document | |
309 * @return the map containing the removed partitioners | |
310 */ | |
311 public static Map removeDocumentPartitioners(IDocument document) { | |
312 Map partitioners= new HashMap(); | |
313 if (document instanceof IDocumentExtension3) { | |
314 IDocumentExtension3 extension3= (IDocumentExtension3) document; | |
315 String[] partitionings= extension3.getPartitionings(); | |
316 for (int i= 0; i < partitionings.length; i++) { | |
317 IDocumentPartitioner partitioner= extension3.getDocumentPartitioner(partitionings[i]); | |
318 if (partitioner !is null) { | |
319 extension3.setDocumentPartitioner(partitionings[i], null); | |
320 partitioner.disconnect(); | |
321 partitioners.put(partitionings[i], partitioner); | |
322 } | |
323 } | |
324 } else { | |
325 IDocumentPartitioner partitioner= document.getDocumentPartitioner(); | |
326 if (partitioner !is null) { | |
327 document.setDocumentPartitioner(null); | |
328 partitioner.disconnect(); | |
329 partitioners.put(IDocumentExtension3.DEFAULT_PARTITIONING, partitioner); | |
330 } | |
331 } | |
332 return partitioners; | |
333 } | |
334 | |
335 /** | |
336 * Connects the given document with all document partitioners stored in the given map under | |
337 * their partitioning name. This method cleans the given map. | |
338 * | |
339 * @param document the document | |
340 * @param partitioners the map containing the partitioners to be connected | |
341 * @since 3.0 | |
342 */ | |
343 public static void addDocumentPartitioners(IDocument document, Map partitioners) { | |
344 if (document instanceof IDocumentExtension3) { | |
345 IDocumentExtension3 extension3= (IDocumentExtension3) document; | |
346 Iterator e= partitioners.keySet().iterator(); | |
347 while (e.hasNext()) { | |
348 String partitioning= (String) e.next(); | |
349 IDocumentPartitioner partitioner= (IDocumentPartitioner) partitioners.get(partitioning); | |
350 partitioner.connect(document); | |
351 extension3.setDocumentPartitioner(partitioning, partitioner); | |
352 } | |
353 partitioners.clear(); | |
354 } else { | |
355 IDocumentPartitioner partitioner= (IDocumentPartitioner) partitioners.get(IDocumentExtension3.DEFAULT_PARTITIONING); | |
356 partitioner.connect(document); | |
357 document.setDocumentPartitioner(partitioner); | |
358 } | |
359 } | |
360 | |
361 /** | |
362 * Returns the content type at the given offset of the given document. | |
363 * | |
364 * @param document the document | |
365 * @param partitioning the partitioning to be used | |
366 * @param offset the offset | |
367 * @param preferOpenPartitions <code>true</code> if precedence should be | |
368 * given to a open partition ending at <code>offset</code> over a | |
369 * closed partition starting at <code>offset</code> | |
370 * @return the content type at the given offset of the document | |
371 * @throws BadLocationException if offset is invalid in the document | |
372 * @since 3.0 | |
373 */ | |
374 public static String getContentType(IDocument document, String partitioning, int offset, bool preferOpenPartitions) throws BadLocationException { | |
375 if (document instanceof IDocumentExtension3) { | |
376 IDocumentExtension3 extension3= (IDocumentExtension3) document; | |
377 try { | |
378 return extension3.getContentType(partitioning, offset, preferOpenPartitions); | |
379 } catch (BadPartitioningException x) { | |
380 return IDocument.DEFAULT_CONTENT_TYPE; | |
381 } | |
382 } | |
383 | |
384 return document.getContentType(offset); | |
385 } | |
386 | |
387 /** | |
388 * Returns the partition of the given offset of the given document. | |
389 * | |
390 * @param document the document | |
391 * @param partitioning the partitioning to be used | |
392 * @param offset the offset | |
393 * @param preferOpenPartitions <code>true</code> if precedence should be | |
394 * given to a open partition ending at <code>offset</code> over a | |
395 * closed partition starting at <code>offset</code> | |
396 * @return the content type at the given offset of this viewer's input | |
397 * document | |
398 * @throws BadLocationException if offset is invalid in the given document | |
399 * @since 3.0 | |
400 */ | |
401 public static ITypedRegion getPartition(IDocument document, String partitioning, int offset, bool preferOpenPartitions) throws BadLocationException { | |
402 if (document instanceof IDocumentExtension3) { | |
403 IDocumentExtension3 extension3= (IDocumentExtension3) document; | |
404 try { | |
405 return extension3.getPartition(partitioning, offset, preferOpenPartitions); | |
406 } catch (BadPartitioningException x) { | |
407 return new TypedRegion(0, document.getLength(), IDocument.DEFAULT_CONTENT_TYPE); | |
408 } | |
409 } | |
410 | |
411 return document.getPartition(offset); | |
412 } | |
413 | |
414 /** | |
415 * Computes and returns the partitioning for the given region of the given | |
416 * document for the given partitioning name. | |
417 * | |
418 * @param document the document | |
419 * @param partitioning the partitioning name | |
420 * @param offset the region offset | |
421 * @param length the region length | |
422 * @param includeZeroLengthPartitions whether to include zero-length partitions | |
423 * @return the partitioning for the given region of the given document for | |
424 * the given partitioning name | |
425 * @throws BadLocationException if the given region is invalid for the given | |
426 * document | |
427 * @since 3.0 | |
428 */ | |
429 public static ITypedRegion[] computePartitioning(IDocument document, String partitioning, int offset, int length, bool includeZeroLengthPartitions) throws BadLocationException { | |
430 if (document instanceof IDocumentExtension3) { | |
431 IDocumentExtension3 extension3= (IDocumentExtension3) document; | |
432 try { | |
433 return extension3.computePartitioning(partitioning, offset, length, includeZeroLengthPartitions); | |
434 } catch (BadPartitioningException x) { | |
435 return new ITypedRegion[0]; | |
436 } | |
437 } | |
438 | |
439 return document.computePartitioning(offset, length); | |
440 } | |
441 | |
442 /** | |
443 * Computes and returns the partition managing position categories for the | |
444 * given document or <code>null</code> if this was impossible. | |
445 * | |
446 * @param document the document | |
447 * @return the partition managing position categories or <code>null</code> | |
448 * @since 3.0 | |
449 */ | |
450 public static String[] computePartitionManagingCategories(IDocument document) { | |
451 if (document instanceof IDocumentExtension3) { | |
452 IDocumentExtension3 extension3= (IDocumentExtension3) document; | |
453 String[] partitionings= extension3.getPartitionings(); | |
454 if (partitionings !is null) { | |
455 Set categories= new HashSet(); | |
456 for (int i= 0; i < partitionings.length; i++) { | |
457 IDocumentPartitioner p= extension3.getDocumentPartitioner(partitionings[i]); | |
458 if (p instanceof IDocumentPartitionerExtension2) { | |
459 IDocumentPartitionerExtension2 extension2= (IDocumentPartitionerExtension2) p; | |
460 String[] c= extension2.getManagingPositionCategories(); | |
461 if (c !is null) { | |
462 for (int j= 0; j < c.length; j++) | |
463 categories.add(c[j]); | |
464 } | |
465 } | |
466 } | |
467 String[] result= new String[categories.size()]; | |
468 categories.toArray(result); | |
469 return result; | |
470 } | |
471 } | |
472 return null; | |
473 } | |
474 | |
475 /** | |
476 * Returns the default line delimiter for the given document. This is either the delimiter of the first line, or the platform line delimiter if it is | |
477 * a legal line delimiter or the first one of the legal line delimiters. The default line delimiter should be used when performing document | |
478 * manipulations that span multiple lines. | |
479 * | |
480 * @param document the document | |
481 * @return the document's default line delimiter | |
482 * @since 3.0 | |
483 */ | |
484 public static String getDefaultLineDelimiter(IDocument document) { | |
485 | |
486 if (document instanceof IDocumentExtension4) | |
487 return ((IDocumentExtension4)document).getDefaultLineDelimiter(); | |
488 | |
489 String lineDelimiter= null; | |
490 | |
491 try { | |
492 lineDelimiter= document.getLineDelimiter(0); | |
493 } catch (BadLocationException x) { | |
494 } | |
495 | |
496 if (lineDelimiter !is null) | |
497 return lineDelimiter; | |
498 | |
499 String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$ | |
500 String[] delimiters= document.getLegalLineDelimiters(); | |
501 Assert.isTrue(delimiters.length > 0); | |
502 for (int i= 0; i < delimiters.length; i++) { | |
503 if (delimiters[i].equals(sysLineDelimiter)) { | |
504 lineDelimiter= sysLineDelimiter; | |
505 break; | |
506 } | |
507 } | |
508 | |
509 if (lineDelimiter is null) | |
510 lineDelimiter= delimiters[0]; | |
511 | |
512 return lineDelimiter; | |
513 } | |
514 | |
515 /** | |
516 * Returns <code>true</code> if the two regions overlap. Returns <code>false</code> if one of the | |
517 * arguments is <code>null</code>. | |
518 * | |
519 * @param left the left region | |
520 * @param right the right region | |
521 * @return <code>true</code> if the two regions overlap, <code>false</code> otherwise | |
522 * @since 3.0 | |
523 */ | |
524 public static bool overlaps(IRegion left, IRegion right) { | |
525 | |
526 if (left is null || right is null) | |
527 return false; | |
528 | |
529 int rightEnd= right.getOffset() + right.getLength(); | |
530 int leftEnd= left.getOffset()+ left.getLength(); | |
531 | |
532 if (right.getLength() > 0) { | |
533 if (left.getLength() > 0) | |
534 return left.getOffset() < rightEnd && right.getOffset() < leftEnd; | |
535 return right.getOffset() <= left.getOffset() && left.getOffset() < rightEnd; | |
536 } | |
537 | |
538 if (left.getLength() > 0) | |
539 return left.getOffset() <= right.getOffset() && right.getOffset() < leftEnd; | |
540 | |
541 return left.getOffset() is right.getOffset(); | |
542 } | |
543 | |
544 /** | |
545 * Returns a copy of the given string array. | |
546 * | |
547 * @param array the string array to be copied | |
548 * @return a copy of the given string array or <code>null</code> when <code>array</code> is <code>null</code> | |
549 * @since 3.1 | |
550 */ | |
551 public static String[] copy(String[] array) { | |
552 if (array !is null) { | |
553 String[] copy= new String[array.length]; | |
554 System.arraycopy(array, 0, copy, 0, array.length); | |
555 return copy; | |
556 } | |
557 return null; | |
558 } | |
559 | |
560 /** | |
561 * Returns a copy of the given integer array. | |
562 * | |
563 * @param array the integer array to be copied | |
564 * @return a copy of the given integer array or <code>null</code> when <code>array</code> is <code>null</code> | |
565 * @since 3.1 | |
566 */ | |
567 public static int[] copy(int[] array) { | |
568 if (array !is null) { | |
569 int[] copy= new int[array.length]; | |
570 System.arraycopy(array, 0, copy, 0, array.length); | |
571 return copy; | |
572 } | |
573 return null; | |
574 } | |
575 } |