Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 74fe1f61189050e6fe3228e5da575151ecee2b4d (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
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
/*******************************************************************************
 * Copyright (c) 2002, 2005 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 org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.IArchive;
import org.eclipse.cdt.core.model.IArchiveContainer;
import org.eclipse.cdt.core.model.IBinary;
import org.eclipse.cdt.core.model.IBinaryContainer;
import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICElementDelta;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ISourceRoot;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.IPath;

/**
 * This class is used by <code>CModelManager</code> to convert
 * <code>IResourceDelta</code>s into <code>ICElementDelta</code>s.
 * It also does some processing on the <code>CElement</code>s involved
 * (e.g. closing them or updating classpaths).
 */
public class DeltaProcessor {
	
	/**
	 * The <code>CElementDelta</code> corresponding to the <code>IResourceDelta</code> being translated.
	 */
	protected CElementDelta fCurrentDelta;
	
	/* The C element that was last created (see createElement(IResource). 
	 * This is used as a stack of C elements (using getParent() to pop it, and 
	 * using the various get*(...) to push it. */
	ICElement currentElement;
	
	static final ICElementDelta[] NO_DELTA = new ICElementDelta[0];

	public static boolean VERBOSE = false;

	// Hold on the element bein renamed.
	ICElement movedFromElement = null;

	/**
	 * Creates the create corresponding to this resource.
	 * Returns null if none was found.
	 */
	protected ICElement createElement(IResource resource) {
		if (resource == null) {
			return null;
		}

		CModelManager manager = CModelManager.getDefault();

		boolean shouldProcess = true;
		
		// Check for C nature or if the was a CNature
		if (!(resource instanceof IWorkspaceRoot)) {
			IProject project = resource.getProject();
			if (!(CoreModel.hasCNature(project) || CoreModel.hasCCNature(project))) {
				shouldProcess = false;
				CModel root = manager.getCModel();
				CModelInfo rootInfo = (CModelInfo)manager.peekAtInfo(root);
				if (rootInfo != null) {
					ICElement[] celements = rootInfo.getChildren();
					for (int i = 0; i < celements.length; i++) {
						IResource r = celements[i].getResource();
						if (project.equals(r)) {
							shouldProcess = true;
						}
					}
				}
			}
		}

		if (!shouldProcess) {
			return null;
		}

		ICElement celement = manager.create(resource, null);

		// BUG 36424:
		// The Binary may only be visible in the BinaryContainers
		try {
			if (celement == null && resource.getType() == IResource.FILE) {
				ICElement[] children;
				ICProject cproj = manager.create(resource.getProject());
				if (cproj != null && cproj.isOpen()) {
					IBinaryContainer bin = cproj.getBinaryContainer();
					if (bin.isOpen()) {
						children = ((CElement)bin).getElementInfo().getChildren();
						for (int i = 0; i < children.length; i++) {
							IResource res = children[i].getResource();
							if (resource.equals(res)) {
								celement = children[i];
								break;
							}
						}
					}
				}
			}
			// BUG 36424:
			// The Archive may only be visible in the ArchiveContainers
			if (celement == null && resource.getType() == IResource.FILE) {
				ICElement[] children;
				ICProject cproj = manager.create(resource.getProject());
				if (cproj != null && cproj.isOpen()) {
					IArchiveContainer ar = cproj.getArchiveContainer();
					if (ar.isOpen()) {
						children = ((CElement)ar).getElementInfo().getChildren();
						for (int i = 0; i < children.length; i++) {
							IResource res = children[i].getResource();
							if (resource.equals(res)) {
								celement = children[i];
								break;
							}
						}
					}
				}				
			}
			//  It is not a C resource if the parent is a Binary/ArchiveContainer
			// But we have to release too.
			if (celement != null && resource.getType() == IResource.FILE) {
				ICElement parent = celement.getParent();
				if (parent instanceof IArchiveContainer || parent instanceof IBinaryContainer) {
					releaseCElement(celement);
					celement = null;
				}
			}
		} catch (CModelException e) {
			return null;
		}
		return celement;
	}

	/**
	 * Adds the given child handle to its parent's cache of children. 
	 */
	protected void addToParentInfo(Openable child) throws CModelException {
		Openable parent = (Openable) child.getParent();
		if (parent != null && parent.isOpen()) {
			CElementInfo info = parent.getElementInfo();
			// Check if the element exits
			if (!info.includesChild(child)) {
				info.addChild(child);
			}
		}
	}

	/**
	 * Removes the given element from its parents cache of children. If the
	 * element does not have a parent, or the parent is not currently open,
	 * this has no effect. 
	 */
	private void removeFromParentInfo(ICElement child) throws CModelException {
		CModelManager factory = CModelManager.getDefault();

		// Remove the child from the parent list.
		ICElement parent = child.getParent();
		if (parent != null && parent instanceof Parent && factory.peekAtInfo(parent) != null) {
			((Parent)parent).removeChild(child);
		}
	}

	/**
	 * Release the Element and cleaning.
	 */
	protected void releaseCElement(ICElement celement) throws CModelException {
		CModelManager factory = CModelManager.getDefault();
		int type = celement.getElementType();
		if (type == ICElement.C_ARCHIVE) {
			ICProject cproject = celement.getCProject();
			IArchiveContainer container = cproject.getArchiveContainer();
			fCurrentDelta.changed(container, ICElementDelta.CHANGED);		
		} else if (type == ICElement.C_BINARY) {
			ICProject cproject = celement.getCProject();
			IBinaryContainer container = cproject.getBinaryContainer();
			fCurrentDelta.changed(container, ICElementDelta.CHANGED);
		} else {
			// If an entire folder was deleted we need to update the
			// BinaryContainer/ArchiveContainer also.
			ICProject cproject = celement.getCProject();
			CProjectInfo pinfo = (CProjectInfo)factory.peekAtInfo(cproject);
			if (pinfo != null && pinfo.vBin != null) {
				if (factory.peekAtInfo(pinfo.vBin) != null) {
					ICElement[] bins = pinfo.vBin.getChildren();
					for (int i = 0; i < bins.length; i++) {
						if (celement.getPath().isPrefixOf(bins[i].getPath())) {
							fCurrentDelta.changed(pinfo.vBin, ICElementDelta.CHANGED);
						}
					}
				}
			}
			if (pinfo != null && pinfo.vLib != null) {
				if (factory.peekAtInfo(pinfo.vLib) != null) {
					ICElement[] ars = pinfo.vLib.getChildren();
					for (int i = 0; i < ars.length; i++) {
						if (celement.getPath().isPrefixOf(ars[i].getPath())) {
							fCurrentDelta.changed(pinfo.vBin, ICElementDelta.CHANGED);
						}
					}
				}
			}
		}
		removeFromParentInfo(celement);
		factory.releaseCElement(celement);
	}

	/**
	 * Creates the create corresponding to this resource.
	 * Returns null if none was found.
	 */
	protected ICElement createElement(IPath path) {
		return CModelManager.getDefault().create(path);
	}

	/**
	 * Processing for an element that has been added:<ul>
	 * <li>If the element is a project, do nothing, and do not process
	 * children, as when a project is created it does not yet have any
	 * natures - specifically a java nature.
	 * <li>If the elemet is not a project, process it as added (see
	 * <code>basicElementAdded</code>.
	 * </ul>
	 */
	protected void elementAdded(ICElement element, IResourceDelta delta) throws CModelException {

		if (element instanceof Openable) {
			addToParentInfo((Openable)element);
		}
		if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
			//ICElement movedFromElement = createElement(delta.getMovedFromPath());
			if  (movedFromElement == null) {
				movedFromElement = createElement(delta.getMovedFromPath());
			}
			fCurrentDelta.movedTo(element, movedFromElement);
			movedFromElement = null;
		} else {
			fCurrentDelta.added(element);
		}
	}

	/**
	 * Processing for the closing of an element - there are two cases:<ul>
	 * <li>when a project is closed (in the platform sense), the
	 * 		CModel reports this as if the CProject has been removed.
	 * <li>otherwise, the CModel reports this
	 *		as a the element being closed (CHANGED + F_CLOSED).
	 * </ul>
	 * <p>In both cases, the children of the element are not processed. When
	 * a resource is closed, the platform reports all children as removed. This
	 * would effectively delete the classpath if we processed children.
	 */
	protected void elementClosed(ICElement element, IResourceDelta delta) throws CModelException {

		if (element.getElementType() == ICElement.C_PROJECT) {
			// treat project closing as removal
			elementRemoved(element, delta);
			CModelInfo rootInfo = (CModelInfo)CModelManager.getDefault().getCModel().getElementInfo();
			rootInfo.setNonCResources(null);
		} else {
			fCurrentDelta.closed(element);
		}
	}

	/**
	 * Processing for the opening of an element - there are two cases:<ul>
	 * <li>when a project is opened (in the platform sense), the
	 * 		CModel reports this as if the CProject has been added.
	 * <li>otherwise, the CModel reports this
	 *		as a the element being opened (CHANGED + F_CLOSED).
	 * </ul>
	 */
	protected void elementOpened(ICElement element, IResourceDelta delta) throws CModelException {

		if (element.getElementType() == ICElement.C_PROJECT) {
			// treat project opening as addition
			if (hasCNature(delta.getResource())) {
				elementAdded(element, delta);
			}
			CModelInfo rootInfo = (CModelInfo)CModelManager.getDefault().getCModel().getElementInfo();
			rootInfo.setNonCResources(null);

		} else {
			fCurrentDelta.opened(element);
		}
	}

	/*
	 * Closes the given element, which removes it from the cache of open elements.
	 */
	private void close(Openable element) {
		try {
			element.close();
		} catch (CModelException e) {
			// do nothing
		}
	}

	/**
	 * This is use to remove the cache info for IArchive and IBinary
	 * We can use IBinary.close() doing this will remove the binary
	 * for the virtual binary/archive containers.
	 * @param celement
	 */
	private void closeBinary(ICElement celement) {
		CModelManager factory = CModelManager.getDefault();
		CElementInfo pinfo = (CElementInfo)factory.peekAtInfo(celement);
		if (pinfo != null) {
			ICElement[] celems = pinfo.getChildren();
			for (int i = 0; i < celems.length; ++i) {
				closeBinary(celems[i]);
			}
			factory.removeInfo(celement);
		}
	}

	/**
	 * Generic processing for elements with changed contents:<ul>
	 * <li>The element is closed such that any subsequent accesses will re-open
	 * the element reflecting its new structure.
	 * <li>An entry is made in the delta reporting a content change (K_CHANGE with F_CONTENT flag set).
	 * </ul>
	 */
	protected void elementChanged(ICElement element, IResourceDelta delta) {
		// For Binary/Archive We can not call close() to do the work
		// closing will remove the element from the {Binary,Archive}Container
		// We nee to clear the cache explicitely
		if (element instanceof IBinary || element instanceof IArchive) {
			closeBinary(element);
		} else if (element instanceof Openable) {
			close((Openable)element);
		}
		fCurrentDelta.changed(element, ICElementDelta.F_CONTENT);
	}

	/**
	 * Generic processing for a removed element:<ul>
	 * <li>Close the element, removing its structure from the cache
	 * <li>Remove the element from its parent's cache of children
	 * <li>Add a REMOVED entry in the delta
	 * </ul>
	 */
	protected void elementRemoved(ICElement element, IResourceDelta delta) throws CModelException {
		if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
			IPath movedToPath = delta.getMovedToPath();
			// create the moved to element
			ICElement movedToElement = createElement(movedToPath);
			if (movedToElement == null) {
				// moved outside
				fCurrentDelta.removed(element);
			} else {
				movedFromElement = element;
				fCurrentDelta.movedFrom(element, movedToElement);
			}
		} else {
			fCurrentDelta.removed(element);
		}
		releaseCElement(element);
	}

	/**
	 * Filters the generated <code>CElementDelta</code>s to remove those
	 * which should not be fired (because they don't represent a real change
	 * in the C Model).
	 */
	protected ICElementDelta[] filterRealDeltas(ICElementDelta[] deltas) {

		int length = deltas.length;
		ICElementDelta[] realDeltas = null;
		int index = 0;
		for (int i = 0; i < length; i++) {
			CElementDelta delta = (CElementDelta)deltas[i];
			if (delta == null) {
				continue;
			}
			if (delta.getAffectedChildren().length > 0
				|| delta.getKind() == ICElementDelta.ADDED
				|| delta.getKind() == ICElementDelta.REMOVED
				|| (delta.getFlags() & ICElementDelta.F_CLOSED) != 0
				|| (delta.getFlags() & ICElementDelta.F_OPENED) != 0
				|| delta.resourceDeltasCounter > 0) {

				if (realDeltas == null) {
					realDeltas = new ICElementDelta[length];
				}
				realDeltas[index++] = delta;
			}
		}
		if (index > 0) {
			ICElementDelta[] result = new ICElementDelta[index];
			System.arraycopy(realDeltas, 0, result, 0, index);
			return result;
		}
		return NO_DELTA;
	}

	/**
	 * Returns true if the given resource is contained in an open project
	 * with a java nature, otherwise false.
	 */
	protected boolean hasCNature(IResource resource) {
		// ensure the project has a C nature (if open)
		IProject project = resource.getProject();
		if (project.isOpen()) {
			return CoreModel.hasCNature(project);
		}
		return false;
	}

	/**
	 * Converts a <code>IResourceDelta</code> rooted in a <code>Workspace</code> into
	 * the corresponding set of <code>ICElementDelta</code>, rooted in the
	 * relevant <code>CModel</code>s.
	 */
	public ICElementDelta[] processResourceDelta(IResourceDelta changes) {

		try {
			ICElement root = CModelManager.getDefault().getCModel();			
			// get the workspace delta, and start processing there.
			IResourceDelta[] deltas = changes.getAffectedChildren();
			ICElementDelta[] translatedDeltas = new CElementDelta[deltas.length];
			//System.out.println("delta.length: " + deltas.length);
			for (int i = 0; i < deltas.length; i++) {
				IResourceDelta delta = deltas[i];
				fCurrentDelta = new CElementDelta(root);
				traverseDelta(root, delta); // traverse delta
				translatedDeltas[i] = fCurrentDelta;
			}
			return filterRealDeltas(translatedDeltas);
		} finally {
		}
	}
	
	/**
	 * Converts an <code>IResourceDelta</code> and its children into
	 * the corresponding <code>ICElementDelta</code>s.
	 * Return whether the delta corresponds to a resource on the classpath.
	 * If it is not a resource on the classpath, it will be added as a non-java
	 * resource by the sender of this method.
	 */
	protected void traverseDelta(ICElement parent, IResourceDelta delta) {
		boolean updateChildren = true;
		try {
			IResource resource = delta.getResource();
			ICElement current = createElement(resource);
			updateChildren = updateCurrentDeltaAndIndex(current, delta);
			if (current == null || current instanceof ISourceRoot) {
				nonCResourcesChanged(parent, delta);
			} else if (current instanceof ICProject) {
				ICProject cprj = (ICProject)current;
				CModel cModel = CModelManager.getDefault().getCModel();
				if (!cprj.getProject().isOpen() || cModel.findCProject(cprj.getProject()) == null) {
					nonCResourcesChanged(parent, delta);
				}
			}
			if (current != null) {
				parent = current;
			}
		} catch (CModelException e) {
		}
		if (updateChildren){
			IResourceDelta [] children = delta.getAffectedChildren();
			for (int i = 0; i < children.length; i++) {
				traverseDelta(parent, children[i]);
			}
		}
	}

	/**
	 * Add the resource delta to the right CElementDelta tree.
	 * @param parent
	 * @param delta
	 */
	protected void nonCResourcesChanged(ICElement parent, IResourceDelta delta) throws CModelException {
		if (parent instanceof Openable && ((Openable)parent).isOpen()) {			
			CElementInfo info = ((Openable)parent).getElementInfo();
			switch (parent.getElementType()) {
			case ICElement.C_MODEL:
				((CModelInfo)info).setNonCResources(null);
				fCurrentDelta.addResourceDelta(delta);
				return;
			case ICElement.C_PROJECT: {
				((CProjectInfo)info).setNonCResources(null);
				// deal with project == sourceroot.  For that case the parent could have been the sourceroot
				// so we must update the sourceroot nonCResource array also.
				ICProject cproject = (ICProject)parent;
				ISourceRoot[] roots = cproject.getAllSourceRoots();
				for (int i = 0; i < roots.length; i++) {
					IResource r = roots[i].getResource();
					if (r instanceof IProject) {
						CElementInfo cinfo = (CElementInfo) CModelManager.getDefault().peekAtInfo(roots[i]);
						if (cinfo instanceof CContainerInfo) {
							((CContainerInfo)cinfo).setNonCResources(null);
						}
					}
				}
				break;
			}
			case ICElement.C_CCONTAINER:
				((CContainerInfo)info).setNonCResources(null);
				break;
			}
		}
		CElementDelta elementDelta = fCurrentDelta.find(parent);
		if (elementDelta == null) {
			fCurrentDelta.changed(parent, ICElementDelta.F_CONTENT);
			elementDelta = fCurrentDelta.find(parent);
			if (elementDelta != null) {
				elementDelta.addResourceDelta(delta);
			}
		} else {
			elementDelta.addResourceDelta(delta);
		}
	}

	/*
	 * Update the current delta (ie. add/remove/change the given element) and update the
	 * correponding index.
	 * Returns whether the children of the given delta must be processed.
	 * @throws a CModelException if the delta doesn't correspond to a c element of the given type.
	 */
	private boolean updateCurrentDeltaAndIndex(ICElement element, IResourceDelta delta) throws CModelException {

		IResource resource = delta.getResource();

		switch (delta.getKind()) {
			case IResourceDelta.ADDED :
				if (element != null) {
					elementAdded(element, delta);
					return element instanceof ICContainer;
				}
				return false;

			case IResourceDelta.REMOVED :
				if (element != null) {
					elementRemoved(element, delta);
				}
				return element instanceof ICContainer;

			case IResourceDelta.CHANGED :
				int flags = delta.getFlags();
				if ((flags & IResourceDelta.CONTENT) != 0) {
					// content has changed
					if (element != null) {
						elementChanged(element, delta);
					}
				} else if (resource.getType() == IResource.PROJECT) {
					if ((flags & IResourceDelta.OPEN) != 0) {
						// project has been opened or closed
						IProject project = (IProject)resource;
						if (element != null) {
							if (project.isOpen()) {
								elementOpened(element, delta);
								return false;
							}
							elementClosed(element, delta);
							//Don't process children
							return false; 
						}
					} 
					if ((flags & IResourceDelta.DESCRIPTION) != 0) {
						IProject res = (IProject)delta.getResource();
						CModel cModel = CModelManager.getDefault().getCModel();
						boolean wasCProject = cModel.findCProject(res) != null;
						boolean isCProject = CProject.hasCNature(res);
						if (wasCProject != isCProject) {
							// project's nature has been added or removed
							if (element != null) {
								// note its resources are still visible as roots to other projects
								if (isCProject) {
									elementOpened(element, delta);
								} else {
									elementRemoved(element, delta);
								}
								return true;
							}
						}
					}
				}
				return true;
		}
		return true;
	}

}

Back to the top