Skip to main content
summaryrefslogtreecommitdiffstats
blob: 1d8565406d72b8bd63e5232cdbd8eaccd18964b4 (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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/*******************************************************************************
 * Copyright (c) 2006 Sybase, Inc. 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:
 *     Sybase, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.jst.pagedesigner.range;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.gef.EditPart;
import org.eclipse.jst.pagedesigner.parts.DocumentEditPart;
import org.eclipse.jst.pagedesigner.viewer.DesignPosition;
import org.eclipse.jst.pagedesigner.viewer.DesignRange;

/**
 * @author mengbo
 */
public class RangeUtil {
	/**
	 * append the child after the reference node as next sibling.
	 * 
	 * @param child
	 *            can't be null
	 * @param reference
	 *            can't be null
	 * @return ??
	 */
    //TODO: dead
//	private static Node appendAfter(Node child, Node reference) {
//		Node next = reference.getNextSibling();
//		if (next == null)
//        {
//			return reference.getParentNode().appendChild(child);
//        }
//        return reference.getParentNode().insertBefore(child, next);
//	}

	/**
	 * @param child
	 * @param reference
	 * @return ??
	 */
    // TODO: dead
//	private static Node insertBefore(Node child, Node reference) {
//		return reference.getParentNode().insertBefore(child, reference);
//	}

	/**
	 * Insert a node into the specified position. The node can be an element or
	 * DocumentFragment.
	 * 
	 * @param node
	 * @param position
	 */
	// TODO: dead
//	private static Node insertElement(DesignPosition position, Element node) {
//		EditPart containerEditPart = position.getContainerPart();
//		int offset = position.getOffset();
//
//		if (containerEditPart instanceof TextEditPart) {
//			TextEditPart textPart = (TextEditPart) containerEditPart;
//			String textData = textPart.getTextData();
//			Node textNode = (Node) textPart.getModel();
//			if (offset == 0)
//				return insertBefore(node, textNode);
//			else if (offset == textData.length())
//				return appendAfter(node, textNode);
//			else {
//				// inserting the element in the middle of text.
//				String before = textData.substring(0, offset);
//				String after = textData.substring(offset);
//
//				// XXX: don't know whether setNodeValue() will do all those
//				// escape or not.
//				textNode.setNodeValue(after);
//				Node newnode = insertBefore(node, textNode);
//
//				// XXX: don't know whether createTextNode() will do all those
//				// escape or not
//				Text t = textNode.getOwnerDocument().createTextNode(before);
//
//				insertBefore(t, newnode);
//				return newnode;
//			}
//		}
//        return insertIntoEditPart(containerEditPart, node, offset);
//	}

	/**
	 * @param containerEditPart
	 * @param node
	 * @param offset
	 * @return
	 */
	// TODO: dead
//	private static Node insertIntoEditPart(EditPart containerEditPart,
//			Node node, int offset) {
//		Node parent = (Node) containerEditPart.getModel();
//		List childParts = containerEditPart.getChildren();
//		if (offset >= childParts.size()) {
//			// to the end of parent
//			return parent.appendChild(node);
//		}
//        Node child = (Node) ((EditPart) childParts.get(offset)).getModel();
//        return insertBefore(node, child);
//	}

	// TODO: dead
//	private static TextPosition insertText(DesignPosition position, String data) {
//		// TODO: never read EditPart containerEditPart = position.getContainerPart();
//
//		position = moveIntoText(position);
//		int offset = position.getOffset();
//
//		if (position.getContainerPart() instanceof TextEditPart) {
//			// it is guaranteeed that now the containing edit part is text node.
//			TextEditPart textPart = (TextEditPart) position.getContainerPart();
//			String textData = textPart.getTextData();
//			String before = textData.substring(0, offset);
//			String after = textData.substring(offset);
//			if (data.startsWith(" ") && before.endsWith(" ")) {
//				before = before.substring(0, before.length() - 1) + " ";
//			}
//			if (after.startsWith(" ") && data.endsWith(" ")) {
//				data = data.substring(0, data.length() - 1) + (char) 160;
//			}
//			String nextData = before + data + after;
//			IDOMText text = (IDOMText) textPart.getModel();
//			text.setData(nextData);
//			return new TextPosition(text, offset + data.length());
//		}
//        // can't merge into a neighboring text node. So create a text node
//        // of it's own
//        EditPart part = position.getContainerPart();
//        Node parent = (Node) part.getModel();
//        Text text = parent.getOwnerDocument().createTextNode(data);
//        insertIntoEditPart(part, text, offset);
//        return new TextPosition((IDOMText) text, offset);
//	}

	/**
	 * Try to make the position move into a text node.
	 * 
	 * @param position
	 * @return
	 */
    // TODO: dead
//	private static DesignPosition moveIntoText(DesignPosition position) {
//		EditPart container = position.getContainerPart();
//		if (container instanceof TextEditPart)
//			return position;
//		if (position.getOffset() > 0) {
//			EditPart pre = (EditPart) container.getChildren().get(
//					position.getOffset() - 1);
//			if (pre instanceof TextEditPart) {
//				return new DesignPosition(pre, ((TextEditPart) pre)
//						.getTextData().length());
//			}
//		}
//		if (position.getOffset() < container.getChildren().size()) {
//			EditPart next = (EditPart) container.getChildren().get(
//					position.getOffset());
//			if (next instanceof TextEditPart) {
//				return new DesignPosition(next, 0);
//			}
//		}
//		return position;
//	}

	/**
	 * try to move the position up to not inside a text. if the position is at 0
	 * index or last index of a text node, then try to move it up.
	 * 
	 * @param position
	 * @return
	 */
    // TODO: dead
//	private static DesignPosition moveOutFromText(DesignPosition position) {
//		EditPart container = position.getContainerPart();
//		if (container instanceof TextEditPart) {
//			int offset = position.getOffset();
//			String text = ((TextEditPart) container).getTextData();
//			if (offset == 0) {
//				return new DesignPosition(container.getParent(), container
//						.getParent().getChildren().indexOf(container));
//			} else if (offset == text.length()) {
//				return new DesignPosition(container.getParent(), container
//						.getParent().getChildren().indexOf(container) + 1);
//			}
//		}
//		return position;
//	}

//	private static void insertDocumentFragment(DesignPosition position,
//			DocumentFragment fragment) {
//		// FIXME: NOT DONE.
//	}

	/**
	 * Test whether the range intersect with the part.
	 * 
	 * @param range
	 * @param part
	 * @return true if thereis an intersection
	 */
	public static boolean intersect(DesignRange range, EditPart part) {
		if (range == null || !range.isValid())
			return false;
		range = normalize(range);
		if (part instanceof DocumentEditPart)
			return true;
		EditPart parent = part.getParent();
		int index = parent.getChildren().indexOf(part);
		DesignPosition left = new DesignPosition(parent, index);
		DesignPosition right = new DesignPosition(parent, index + 1);
		int compare = compareDesignPosition(left, range.getEndPosition());
		if (compare == 1 || compare == 0 || compare == Integer.MIN_VALUE)
			return false;

		compare = compareDesignPosition(right, range.getStartPosition());
		if (compare == -1 || compare == 0 || compare == Integer.MIN_VALUE)
			return false;

		return true;
	}

	/**
	 * make sure the start position is before end position. If the original
	 * range is already normalized, then the original range will be returned
	 * without constructing a new one.
	 * 
	 * @param range
	 * @return the normalized range
	 */
	public static DesignRange normalize(DesignRange range) {
		if (range == null || !range.isValid()) {
			return range;
		}
		int result = compareDesignPosition(range.getStartPosition(), range
				.getEndPosition());
		if (result == 1)
        {
			return new DesignRange(range.getEndPosition(), range
					.getStartPosition());
        }
        return range;
	}

	/**
	 * 
	 * @param p1
	 * @param p2
	 * @return 0 means equal. 1 Means p1 is after p2. -1 means p1 is before p2.
	 *         Integer.MIN_VALUE means some error and can't compare.
	 */
	private static int compareDesignPosition(DesignPosition p1, DesignPosition p2) {
		if (!p1.isValid() || !p2.isValid())
			return Integer.MIN_VALUE;
		if (p1.equals(p2))
			return 0;
		int offset1 = p1.getOffset();
		int offset2 = p2.getOffset();
		List a1 = getAncesters(p1.getContainerPart());
		List a2 = getAncesters(p2.getContainerPart());
		if (a1 == null || a2 == null)
			return Integer.MIN_VALUE;
		if (a1.get(0) != a2.get(0))
			return Integer.MIN_VALUE; // not same DocumentEditPart
		for (int i = 1;; i++) {
			EditPart p1a = (EditPart) a1.get(i);
			EditPart p2a = (EditPart) a2.get(i);
			if (p1a == p2a) {
				if (p1a != null)
                {
					continue; // same ancester
                }
                // both are null. just compare the offset.
                return offset1 < offset2 ? -1
                		: (offset1 == offset2 ? 0 : 1);
			}
			// p1a != p2a. now we can just compare p1a and p2a to decide the
			// order.
			if (p1a != null)
				offset1 = p1a.getParent().getChildren().indexOf(p1a);
			if (p2a != null)
				offset2 = p2a.getParent().getChildren().indexOf(p2a);
			if ((p1a == null && p2a == null) || (p1a != null && p2a != null)) {
				return offset1 < offset2 ? -1 : (offset1 == offset2 ? 0 : 1);
			} else if (p1a == null) {
				return offset1 <= offset2 ? -1 : 1;
			} else {
				return offset1 >= offset2 ? 1 : -1;
			}
		}
	}

	/**
	 * Get a list of ancester nodes starting from the DocumentEditPart till the
	 * node.
	 * 
	 * @param part
	 * @return
	 */
	private static List getAncesters(EditPart part) {
		List list = new ArrayList();
		while (part != null) {
			list.add(part);
			if (part instanceof DocumentEditPart)
            {
				break;
            }
			part = part.getParent();
		}
		if (part == null) {
			// if part ==null, means we didn't find a DocumentEditPart,
			// something must be wrong.
			return null;
		}
		// reverse to make it starting from the docuemnteditpart node.
		Collections.reverse(list);
		list.add(null); // add an null terminator.
		return list;
	}

	/**
	 * find the smallest common ancester of two edit part.
	 * 
	 * @param part1
	 * @param part2
	 * @return
	 */
	private static EditPart findCommonAncester(EditPart part1, EditPart part2) {
		if (part1 == part2) {
			return part1;
		}
		List list1 = getAncesters(part1);
		if (list1 == null)
			return null;
		List list2 = getAncesters(part2);
		if (list2 == null)
			return null;
		if (list1.get(0) != list2.get(0))
			return null;
		EditPart common = (EditPart) list1.get(0);
		for (int i = 1;; i++) {
			EditPart p1 = (EditPart) list1.get(i);
			EditPart p2 = (EditPart) list2.get(i);
			if (p1 == null || p2 == null)
				return common;
			if (p1 != p2)
				return common;
			common = p1;
		}

	}

	/**
	 * @param range
	 * @return the common ancestor
	 */
	public static EditPart findCommonAncestor(DesignRange range) {
		if (!range.isValid()) {
			return null;
		}
		DesignPosition startPosition = range.getStartPosition();
		DesignPosition endPosition = range.getEndPosition();
		return findCommonAncester(startPosition.getContainerPart(), endPosition
				.getContainerPart());
	}
}

Back to the top