Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: bb7451b908515f1ccaa9e22b24474396596688e8 (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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
/*******************************************************************************
 * Copyright (c) 2002, 2008 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:
 * Rational Software - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.internal.core.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICElementDelta;
import org.eclipse.cdt.core.model.IParent;

/**
 * A C element delta biulder creates a C element delta on a C element between
 * the version of the C element at the time the comparator was created and the
 * current version of the C element.
 *
 * It performs this operation by locally caching the contents of 
 * the C element when it is created. When the method buildDeltas() is called, it
 * creates a delta over the cached contents and the new contents.
 * 
 * This class is similar to the JDT CElementDeltaBuilder class.
 */

public class CElementDeltaBuilder {
	
	/**
	 * The c element handle
	 */
	ICElement cElement;

	/**
	 * The maximum depth in the C element children we should look into
	 */
	int maxDepth = Integer.MAX_VALUE;

	/**
	 * The old handle to info relationships
	 */
	Map<ICElement, CElementInfo> infos;

	/**
	 * The old position info
	 */
	Map<ICElement, ListItem> oldPositions;

	/**
	 * The new position info
	 */
	Map<ICElement, ListItem> newPositions;

	/**
	 * Change delta
	 */
	CElementDelta delta;

	/**
	 * List of added elements
	 */
	ArrayList<ICElement> added;

	/**
	 * List of removed elements
	 */
	ArrayList<ICElement> removed;
	
	/**
	 * Doubly linked list item
	 */
	class ListItem {
		public ICElement previous;
		public ICElement next;

		public ListItem(ICElement previous, ICElement next) {
			this.previous = previous;
			this.next = next;
		}
	}
/**
 * Creates a C element comparator on a C element
 * looking as deep as necessary.
 */
public CElementDeltaBuilder(ICElement CElement) {
	this.cElement = CElement;
	this.initialize();
	this.recordElementInfo(CElement,0);
}
/**
 * Creates a C element comparator on a C element
 * looking only 'maxDepth' levels deep.
 */
public CElementDeltaBuilder(ICElement cElement, int maxDepth) {
	this.cElement = cElement;
	this.maxDepth = maxDepth;
	this.initialize();
	this.recordElementInfo(cElement,0);
}
/**
 * Repairs the positioning information
 * after an element has been added
 */
private void added(ICElement element) {
	this.added.add(element);
	ListItem current = this.getNewPosition(element);
	ListItem previous = null, next = null;
	if (current.previous != null)
		previous = this.getNewPosition(current.previous);
	if (current.next != null)
		next = this.getNewPosition(current.next);
	if (previous != null)
		previous.next = current.next;
	if (next != null)
		next.previous = current.previous;
}
/**
 * Builds the C element deltas between the old content of the translation unit
 * and its new content.
 */
public void buildDeltas() throws CModelException {
	this.recordNewPositions(this.cElement, 0);
	this.findAdditions(this.cElement, 0);
	this.findDeletions();
	this.findChangesInPositioning(this.cElement, 0);
	this.trimDelta(this.delta);
}
/**
 * Finds elements which have been added or changed.
 */
private void findAdditions(ICElement newElement, int depth) throws CModelException {
	CElementInfo oldInfo = this.getElementInfo(newElement);
	if (oldInfo == null && depth < this.maxDepth) {
		this.delta.added(newElement);
		added(newElement);
	} else {
		this.removeElementInfo(newElement);
	}
	
	if (depth >= this.maxDepth) {
		// mark element as changed
		this.delta.changed(newElement, ICElementDelta.F_CONTENT);
		return;
	}

	CElementInfo newInfo = null;
	newInfo = ((CElement)newElement).getElementInfo();
	
	this.findContentChange(oldInfo, newInfo, newElement);
		
	if (oldInfo != null && newElement instanceof IParent) {

		ICElement[] children = newInfo.getChildren();
		if (children != null) {
			int length = children.length;
			for(int i = 0; i < length; i++) {
				this.findAdditions(children[i], depth + 1);
			}
		}		
	}
}
/**
 * Looks for changed positioning of elements.
 */
private void findChangesInPositioning(ICElement element, int depth) throws CModelException {
	if (depth >= this.maxDepth || this.added.contains(element) || this.removed.contains(element))
		return;
		
	if (!isPositionedCorrectly(element)) {
		this.delta.changed(element, ICElementDelta.F_REORDER);
	} 
	
	if (element instanceof IParent) {
		CElementInfo info = null;
		info = ((CElement)element).getElementInfo();

		ICElement[] children = info.getChildren();
		if (children != null) {
			int length = children.length;
			for(int i = 0; i < length; i++) {
				this.findChangesInPositioning(children[i], depth + 1);
			}
		}		
	}
}
/**
 * The elements are equivalent, but might have content changes.
 */
private void findContentChange(CElementInfo oldInfo, CElementInfo newInfo, ICElement newElement) {
	if (oldInfo instanceof SourceManipulationInfo && newInfo instanceof SourceManipulationInfo) {
		SourceManipulationInfo oldSourceInfo = (SourceManipulationInfo) oldInfo;
		SourceManipulationInfo newSourceInfo = (SourceManipulationInfo) newInfo;
		
		if ((oldSourceInfo).getModifiers() != (newSourceInfo).getModifiers()) {
			this.delta.changed(newElement, ICElementDelta.F_MODIFIERS);
		}
		
		// The element info should be able to tell if the contents are the same. 
		if(!oldSourceInfo.hasSameContentsAs(newSourceInfo)){
			this.delta.changed(newElement, ICElementDelta.F_CONTENT);
		}
	}
}
/**
 * Adds removed deltas for any handles left in the table
 */
private void findDeletions() {
	Iterator<ICElement> iter = this.infos.keySet().iterator();
	while(iter.hasNext()) {
		ICElement element = iter.next();
		this.delta.removed(element);
		this.removed(element);
	}
}
private CElementInfo getElementInfo(ICElement element) {
	return this.infos.get(element);
}
private ListItem getNewPosition(ICElement element) {
	return this.newPositions.get(element);
}
private ListItem getOldPosition(ICElement element) {
	return this.oldPositions.get(element);
}
private void initialize() {
	this.infos = new HashMap<ICElement, CElementInfo>(20);
	this.oldPositions = new HashMap<ICElement, ListItem>(20);
	this.newPositions = new HashMap<ICElement, ListItem>(20);
	this.putOldPosition(this.cElement, new ListItem(null, null));
	this.putNewPosition(this.cElement, new ListItem(null, null));
	this.delta = new CElementDelta(cElement);
	
	// if building a delta on a translation unit or below, 
	// it's a fine grained delta
	if (cElement.getElementType() >= ICElement.C_UNIT) {
		this.delta.fineGrained();
	}
	
	this.added = new ArrayList<ICElement>(5);
	this.removed = new ArrayList<ICElement>(5);
}
/**
 * Inserts position information for the elements into the new or old positions table
 */
private void insertPositions(ICElement[] elements, boolean isNew) {
	int length = elements.length;
	ICElement previous = null, current = null, next = (length > 0) ? elements[0] : null;
	for(int i = 0; i < length; i++) {
		previous = current;
		current = next;
		next = (i + 1 < length) ? elements[i + 1] : null;
		if (isNew) {
			this.putNewPosition(current, new ListItem(previous, next));
		} else {
			this.putOldPosition(current, new ListItem(previous, next));
		}
	}
}
/**
 * Returns true if the given elements represent the an equivalent declaration.
 *
 * <p>NOTE: Since this comparison can be done with handle info only,
 * none of the internal calls need to use the locally cached contents
 * of the old translation unit.
 */
//private boolean isIdentical(CElement e1, CElement e2) {
//	if (e1 == null ^ e2 == null)
//		return false;
//	
//	if (e1 == null)
//		return true;
//
//	return e1.isIdentical(e2);					
//}
/**
 * Answers true if the elements position has not changed.
 * Takes into account additions so that elements following
 * new elements will not appear out of place.
 */
private boolean isPositionedCorrectly(ICElement element) {
	ListItem oldListItem = this.getOldPosition(element);
	if (oldListItem == null) return false;
	
	ListItem newListItem = this.getNewPosition(element);
	if (newListItem == null) return false;
	
	ICElement oldPrevious = oldListItem.previous;
	ICElement newPrevious = newListItem.previous;
	
	if (oldPrevious == null) {
		return newPrevious == null;
	}
	return oldPrevious.equals(newPrevious);
}
private void putElementInfo(ICElement element, CElementInfo info) {
	this.infos.put(element, info);
}
private void putNewPosition(ICElement element, ListItem position) {
	this.newPositions.put(element, position);
}
private void putOldPosition(ICElement element, ListItem position) {
	this.oldPositions.put(element, position);
}
/**
 * Records this elements info, and attempts
 * to record the info for the children.
 */
private void recordElementInfo(ICElement element, int depth) {
	if (depth >= this.maxDepth) {
		return;
	}
	CElementInfo info = (CElementInfo)CModelManager.getDefault().getInfo(element);
	if (info == null) // no longer in the C model.
		return;
	this.putElementInfo(element, info);
		
	if (element instanceof IParent) {
		ICElement[] children = info.getChildren();
		if (children != null) {
			insertPositions(children, false);
			for(int i = 0, length = children.length; i < length; i++)
				recordElementInfo(children[i], depth + 1);
		}
	}
}
/**
 * Fills the newPositions hashtable with the new position information
 */
private void recordNewPositions(ICElement newElement, int depth) throws CModelException {
	if (depth < this.maxDepth && newElement instanceof IParent) {
		CElementInfo info = null;
		info = ((CElement)newElement).getElementInfo();

		ICElement[] children = info.getChildren();
		if (children != null) {
			insertPositions(children, true);
			for(int i = 0, length = children.length; i < length; i++) {
				recordNewPositions(children[i], depth + 1);
			}
		}
	}
}
/**
 * Repairs the positioning information
 * after an element has been removed
 */
private void removed(ICElement element) {
	this.removed.add(element);
	ListItem current = this.getOldPosition(element);
	ListItem previous = null, next = null;
	if (current.previous != null)
		previous = this.getOldPosition(current.previous);
	if (current.next != null)
		next = this.getOldPosition(current.next);
	if (previous != null)
		previous.next = current.next;
	if (next != null)
		next.previous = current.previous;
	
}
private void removeElementInfo(ICElement element) {
	this.infos.remove(element);
}
@Override
public String toString() {
	StringBuffer buffer = new StringBuffer();
	buffer.append("Built delta:\n"); //$NON-NLS-1$
	buffer.append(this.delta.toString());
	return buffer.toString();
}
/**
 * Trims deletion deltas to only report the highest level of deletion
 */
private void trimDelta(CElementDelta delta) {
	if (delta.getKind() == ICElementDelta.REMOVED) {
		ICElementDelta[] children = delta.getAffectedChildren();
		for(int i = 0, length = children.length; i < length; i++) {
			delta.removeAffectedChild((CElementDelta)children[i]);
		}
	} else {
		ICElementDelta[] children = delta.getAffectedChildren();
		for(int i = 0, length = children.length; i < length; i++) {
			trimDelta((CElementDelta)children[i]);
		}
	}
}
	
}

Back to the top