Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 91f15174ec46cb4c60a71342cdc036bd4729cbe9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jface.text;



/**
 * Implements a gap managing text store. The gap text store 
 * relies on the assumption that subsequent changes of a document are co-located.
 * The start of the gap is always moved to the location of the last change. The
 * size of the gap varies between the low water mark and the high water mark. <p>
 * This class is not intended to be subclassed.
 */
public class GapTextStore implements ITextStore {
	
	/** The store's content */
	private char[] fContent= new char[0];
	/** Starting index of the gap */
	private int fGapStart= -1;
	/** End index of the gap */
	private int fGapEnd= -1;
	
	/** The high water mark. If the gap is larger than this, it will be shrunken */
	private int fHighWatermark;
	/** The low water mark, If this gap is smaller than this, it will be extended */
	private int fLowWatermark;
	
	/**
	 * Creates a new empty text store using the specified low and high watermarks.
	 *
	 * @param lowWatermark if this gap is ever smaller than this, it will automatically be extended
	 * @param highWatermark if the gap is ever larger than this, it will automatically be shrunken
	 */
	public GapTextStore(int lowWatermark, int highWatermark) {
		Assert.isTrue(lowWatermark < highWatermark);
		fLowWatermark= lowWatermark;
		fHighWatermark= highWatermark;
	}

	/**
	 * Adjusts the gap so that is at the right offset and capable of handling
	 * the addition of a specified number of characters without having to be shifted.
	 * The <code>sizeHint</code> represents the range that will be filled afterwards.
	 * If the gap is already at the right offset, it must only be
	 * resized if it will be no longer between the low and high watermark. However,
	 * on delete (sizeHint &lt; 0) at the edges of the gap, the gap is only enlarged.
	 *
	 * @param offset the offset at which the change happens
	 * @param sizeHint the number of character which will be inserted
	 */
	private void adjustGap(int offset, int sizeHint) {
					
		if (offset == fGapStart) {
			int size= (fGapEnd - fGapStart) - sizeHint;
			if (fLowWatermark <= size && size <= fHighWatermark)
				return;
		}
		
		moveAndResizeGap(offset, sizeHint);
	}
	
	/**
	 * Moves the gap to the specified offset and adjust its size to the
	 * anticipated change size. The given size represents the expected 
	 * range of the gap that will be filled after the gap has been moved.
	 * Thus the gap is resized to actual size + the specified size and
	 * moved to the given offset.
	 *
	 * @param offset the offset where the gap is moved to
	 * @param size the anticipated size of the change
	 */ 
	private void moveAndResizeGap(int offset, int size) {
		
		char[] content= null;
		int oldSize= fGapEnd - fGapStart;
		int newSize= fHighWatermark + size;


		if (newSize < 0) {

			if (oldSize > 0) {
				content= new char[fContent.length - oldSize];
				System.arraycopy(fContent, 0, content, 0, fGapStart);
				System.arraycopy(fContent, fGapEnd, content, fGapStart, content.length - fGapStart);
				fContent= content;
			}
			fGapStart= fGapEnd= offset;
			return;
		}


		content= new char[fContent.length + (newSize - oldSize)];

		int newGapStart= offset;
		int newGapEnd= newGapStart + newSize;

		if (oldSize == 0) {
			
			System.arraycopy(fContent, 0, content, 0, newGapStart);
			System.arraycopy(fContent, newGapStart, content, newGapEnd, content.length - newGapEnd);
		
		} else if (newGapStart < fGapStart) {
			
			int delta= fGapStart - newGapStart;
			System.arraycopy(fContent, 0, content, 0, newGapStart);
			System.arraycopy(fContent, newGapStart, content, newGapEnd, delta);
			System.arraycopy(fContent, fGapEnd, content, newGapEnd + delta, fContent.length - fGapEnd);

		} else {
		
			int delta= newGapStart - fGapStart;
			System.arraycopy(fContent, 0, content, 0, fGapStart);
			System.arraycopy(fContent, fGapEnd, content, fGapStart, delta);
			System.arraycopy(fContent, fGapEnd + delta, content, newGapEnd, content.length - newGapEnd);
		}


		fContent= content;
		fGapStart= newGapStart;
		fGapEnd= newGapEnd;
	}
	
	/*
	 * @see org.eclipse.jface.text.ITextStore#get(int)
	 */
	public char get(int offset) {
		
		if (offset < fGapStart)
			return fContent[offset];

		int gapLength= fGapEnd - fGapStart;
		return fContent[offset + gapLength];
	}
	
	/*
	 * @see org.eclipse.jface.text.ITextStore#get(int, int)
	 */
	public String get(int offset, int length) {

		int end= offset + length;

		if (fContent == null)
			return ""; //$NON-NLS-1$
		
		if (end <= fGapStart)
			return new String(fContent, offset, length);

		if (fGapStart < offset) {
			int gapLength= fGapEnd - fGapStart;
			return new String(fContent, offset + gapLength , length);
		}

		StringBuffer buf= new StringBuffer();
		buf.append(fContent, offset, fGapStart - offset);
		buf.append(fContent, fGapEnd, end - fGapStart);
		return buf.toString();
	}
	
	/*
	 * @see org.eclipse.jface.text.ITextStore#getLength()
	 */
	public int getLength() {
		int length= fGapEnd - fGapStart;
		return (fContent.length - length);
	}
	
	/*
	 * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String)
	 */
	public void replace(int offset, int length, String text) {
		
		int textLength= (text == null ? 0 : text.length());		
		
		// handle delete at the edges of the gap
		if (textLength == 0) {
			if (offset <= fGapStart && offset + length >= fGapStart && fGapStart > -1 && fGapEnd > -1) {
				length -= fGapStart - offset;
				fGapStart= offset;
				fGapEnd += length;
				return;
			}
		}
		
		// move gap
		adjustGap(offset + length, textLength - length);

		// overwrite
		int min= Math.min(textLength, length);
		for (int i= offset, j= 0; i < offset + min; i++, j++)
			fContent[i]= text.charAt(j);

		if (length > textLength) {
			// enlarge the gap
			fGapStart -= (length - textLength);
		} else if (textLength > length) {
			// shrink gap
			fGapStart += (textLength - length);
			for (int i= length; i < textLength; i++)
				fContent[offset + i]= text.charAt(i);
		}	
	}
	
	/**
	 * Sets the content to <code>text</code> and removes the gap
	 * since there are no sensible predictions about 
	 * where the next change will occur.
	 * 
	 * @see ITextStore#set(String)
	 */
	public void set(String text) {
		
		if (text == null)
			text= ""; //$NON-NLS-1$

		fContent= text.toCharArray();

		fGapStart= -1;
		fGapEnd=   -1;
	}
	
	/**
	 * Returns a copy of the content of this text store.
	 * For internal use only.
	 *
	 * @return a copy of the content of this text store 
	 */
	protected String getContentAsString() {
		return new String(fContent);
	}
	
	/**
	 * Returns the start index of the gap managed by this text store.
	 * For internal use only.
	 *
	 * @return the start index of the gap managed by this text store
	 */
	protected int getGapStartIndex() {
		return fGapStart;
	}
	
	/**
	 * Returns the end index of the gap managed by this text store.
	 * For internal use only.
	 *
	 * @return the end index of the gap managed by this text store
	 */
	protected int getGapEndIndex() {
		return fGapEnd;
	}
}

Back to the top