Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: c79124fdc873f024c3996f9f0b383b6ba7de96e6 (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
/*******************************************************************************
 * Copyright (c) 2000, 2010 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;


/**
 * Default implementation of {@link org.eclipse.jface.text.IPositionUpdater}.
 * <p>
 * A default position updater must be configured with the position category whose positions it will
 * update. Other position categories are not affected by this updater.
 * </p>
 * <p>
 * This implementation follows the specification below:
 * </p>
 * <ul>
 * <li>Inserting or deleting text before the position shifts the position accordingly.</li>
 * <li>Inserting text at the position offset shifts the position accordingly.</li>
 * <li>Inserting or deleting text strictly contained by the position shrinks or stretches the
 * position.</li>
 * <li>Inserting or deleting text after a position does not affect the position.</li>
 * <li>Deleting text which strictly contains the position deletes the position. Note that the
 * position is not deleted if its only shrunken to length zero. To delete a position, the
 * modification must delete from <i>strictly before</i> to <i>strictly after</i> the position.</li>
 * <li>Replacing text contained by the position shrinks or expands the position (but does not shift it),
 * such that the final position contains the original position and the replacing text.</li>
 * <li>Replacing text overlapping the position in other ways is considered as a sequence of first deleting
 * the replaced text and afterwards inserting the new text. Thus, a position is shrunken and can
 * then be shifted (if the replaced text overlaps the offset of the position).</li>
 * </ul>
 * This class can be used as is or be adapted by subclasses. Fields are protected to allow
 * subclasses direct access. Because of the frequency with which position updaters are used this is
 * a performance decision.
 */
public class DefaultPositionUpdater implements IPositionUpdater {

	/** The position category the updater draws responsible for */
	private final String fCategory;

	/** Caches the currently investigated position */
	protected Position fPosition;
	/** Caches the original state of the investigated position */
	protected Position fOriginalPosition= new Position(0, 0);

	/** Caches the offset of the replaced text */
	protected int fOffset;
	/** Caches the length of the replaced text */
	protected int fLength;
	/** Caches the length of the newly inserted text */
	protected int fReplaceLength;
	/** Caches the document */
	protected IDocument fDocument;


	/**
	 * Creates a new default position updater for the given category.
	 *
	 * @param category the category the updater is responsible for
	 */
	public DefaultPositionUpdater(String category) {
		fCategory= category;
	}

	/**
	 * Returns the category this updater is responsible for.
	 *
	 * @return the category this updater is responsible for
	 */
	protected String getCategory() {
		return fCategory;
	}

	/**
	 * Returns whether the current event describes a well formed replace
	 * by which the current position is directly affected.
	 *
	 * @return <code>true</code> the current position is directly affected
	 * @since 3.0
	 */
	protected boolean isAffectingReplace() {
		return fLength > 0 && fReplaceLength > 0 && fPosition.length < fOriginalPosition.length;
	}

	/**
	 * Adapts the currently investigated position to an insertion.
	 */
	protected void adaptToInsert() {

		int myStart= fPosition.offset;
		int myEnd=   fPosition.offset + fPosition.length - 1;
		myEnd= Math.max(myStart, myEnd);

		int yoursStart= fOffset;
		int yoursEnd=   fOffset + fReplaceLength -1;
		yoursEnd= Math.max(yoursStart, yoursEnd);

		if (myEnd < yoursStart)
			return;

		if (myStart < yoursStart)
			fPosition.length += fReplaceLength;
		else
			fPosition.offset += fReplaceLength;
	}

	/**
	 * Adapts the currently investigated position to a deletion.
	 */
	protected void adaptToRemove() {

		int myStart= fPosition.offset;
		int myEnd=   fPosition.offset + fPosition.length -1;
		myEnd= Math.max(myStart, myEnd);

		int yoursStart= fOffset;
		int yoursEnd=   fOffset + fLength -1;
		yoursEnd= Math.max(yoursStart, yoursEnd);

		if (myEnd < yoursStart)
			return;

		if (myStart <= yoursStart) {

			if (yoursEnd <= myEnd)
				fPosition.length -= fLength;
			else
				fPosition.length -= (myEnd - yoursStart +1);

		} else if (yoursStart < myStart) {

			if (yoursEnd < myStart)
				fPosition.offset -= fLength;
			else {
				fPosition.offset -= (myStart - yoursStart);
				fPosition.length -= (yoursEnd - myStart +1);
			}

		}

		// validate position to allowed values
		if (fPosition.offset < 0)
			fPosition.offset= 0;

		if (fPosition.length < 0)
			fPosition.length= 0;
	}

	/**
	 * Adapts the currently investigated position to the replace operation.
	 * First it checks whether the change replaces only a non-zero range inside the range of the position (including the borders).
	 * If not, it performs first the deletion of the previous text and afterwards
	 * the insertion of the new text.
	 */
	protected void adaptToReplace() {

		if (fLength > 0
				&& fPosition.offset <= fOffset
				&& fOffset + fLength <= fPosition.offset + fPosition.length) {

			fPosition.length += fReplaceLength - fLength;

		} else {

			if (fLength >  0)
				adaptToRemove();

			if (fReplaceLength > 0)
				adaptToInsert();
		}
	}

	/**
	 * Determines whether the currently investigated position has been deleted by
	 * the replace operation specified in the current event. If so, it deletes
	 * the position and removes it from the document's position category.
	 *
	 * @return <code>true</code> if position has not been deleted
	 */
	protected boolean notDeleted() {

		if (fOffset < fPosition.offset && (fPosition.offset + fPosition.length < fOffset + fLength)) {

			fPosition.delete();

			try {
				fDocument.removePosition(fCategory, fPosition);
			} catch (BadPositionCategoryException x) {
			}

			return false;
		}

		return true;
	}

	/*
	 * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
	 */
	public void update(DocumentEvent event) {

		try {


			fOffset= event.getOffset();
			fLength= event.getLength();
			fReplaceLength= (event.getText() == null ? 0 : event.getText().length());
			fDocument= event.getDocument();

			Position[] category= fDocument.getPositions(fCategory);
			for (int i= 0; i < category.length; i++) {

				fPosition= category[i];
				fOriginalPosition.offset= fPosition.offset;
				fOriginalPosition.length= fPosition.length;

				if (notDeleted())
					adaptToReplace();
			}

		} catch (BadPositionCategoryException x) {
			// do nothing
		} finally {
			fDocument= null;
		}
	}
}

Back to the top