1
/*******************************************************************************
2
* Copyright (c) 2000, 2008 QNX Software Systems and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* QNX Software Systems - Initial API and implementation
10
*******************************************************************************/
11
package org.eclipse.cdt.internal.core.model;
13
import java.io.InputStream;
14
import java.util.ArrayList;
15
import java.util.HashMap;
16
import java.util.LinkedList;
17
import java.util.List;
19
import org.eclipse.cdt.core.model.CModelException;
20
import org.eclipse.cdt.core.model.ElementChangedEvent;
21
import org.eclipse.cdt.core.model.ICElement;
22
import org.eclipse.cdt.core.model.ICElementDelta;
23
import org.eclipse.cdt.core.model.ICModel;
24
import org.eclipse.cdt.core.model.ICModelStatus;
25
import org.eclipse.cdt.core.model.ICModelStatusConstants;
26
import org.eclipse.cdt.core.model.ICProject;
27
import org.eclipse.cdt.core.model.ISourceReference;
28
import org.eclipse.cdt.core.model.ITranslationUnit;
29
import org.eclipse.cdt.core.model.IWorkingCopy;
30
import org.eclipse.core.resources.IContainer;
31
import org.eclipse.core.resources.IFile;
32
import org.eclipse.core.resources.IFolder;
33
import org.eclipse.core.resources.IResource;
34
import org.eclipse.core.resources.IResourceStatus;
35
import org.eclipse.core.resources.IWorkspace;
36
import org.eclipse.core.resources.IWorkspaceRunnable;
37
import org.eclipse.core.resources.ResourcesPlugin;
38
import org.eclipse.core.runtime.CoreException;
39
import org.eclipse.core.runtime.IPath;
40
import org.eclipse.core.runtime.IProgressMonitor;
41
import org.eclipse.core.runtime.OperationCanceledException;
42
import org.eclipse.core.runtime.Path;
43
import org.eclipse.core.runtime.SubProgressMonitor;
44
import org.eclipse.core.runtime.jobs.ISchedulingRule;
47
* Defines behavior common to all C Model operations
49
public abstract class CModelOperation implements IWorkspaceRunnable, IProgressMonitor {
51
* The elements this operation operates on,
52
* or <code>null</code> if this operation
53
* does not operate on specific elements.
55
protected ICElement[] fElementsToProcess;
58
* The parent elements this operation operates with
59
* or <code>null</code> if this operation
60
* does not operate with specific parent elements.
62
protected ICElement[] parentElements;
65
* An empty collection of <code>ICElement</code>s - the common
66
* empty result if no elements are created, or if this
67
* operation is not actually executed.
69
protected static final ICElement[] fgEmptyResult= new ICElement[] {};
72
* Collection of <code>ICElementDelta</code>s created by this operation.
73
* This collection starts out <code>null</code> and becomes an
74
* array of <code>ICElementDelta</code>s if the operation creates any
75
* deltas. This collection is registered with the C Model notification
76
* manager if the operation completes successfully.
78
protected List<ICElementDelta> fDeltas= null;
81
* The elements created by this operation - empty
82
* until the operation actually creates elements.
84
protected ICElement[] fResultElements= fgEmptyResult;
87
* The progress monitor passed into this operation
89
protected IProgressMonitor fMonitor= null;
92
* A flag indicating whether this operation is nested.
94
protected boolean fNested = false;
97
* Conflict resolution policy - by default do not force (fail on a conflict).
99
protected boolean fForce= false;
102
* Whether the operation has modified resources, and thus whether resource
103
* delta notifcation will happen.
105
protected boolean hasModifiedResource = false;
108
* A per thread stack of java model operations (PerThreadObject of ArrayList).
110
protected final static ThreadLocal<ArrayList<CModelOperation>> operationStacks = new ThreadLocal<ArrayList<CModelOperation>>();
112
protected CModelOperation() {
115
* A common constructor for all C Model operations.
117
protected CModelOperation(ICElement[] elements) {
118
fElementsToProcess = elements;
122
* Common constructor for all C Model operations.
124
protected CModelOperation(ICElement[] elementsToProcess, ICElement[] parentElements) {
125
fElementsToProcess = elementsToProcess;
126
this.parentElements= parentElements;
130
* A common constructor for all C Model operations.
132
protected CModelOperation(ICElement[] elementsToProcess, ICElement[] parentElements, boolean force) {
133
fElementsToProcess = elementsToProcess;
134
this.parentElements= parentElements;
139
* A common constructor for all C Model operations.
141
protected CModelOperation(ICElement[] elements, boolean force) {
142
fElementsToProcess = elements;
147
* Common constructor for all C Model operations.
149
protected CModelOperation(ICElement element) {
150
fElementsToProcess = new ICElement[]{element};
154
* A common constructor for all C Model operations.
156
protected CModelOperation(ICElement element, boolean force) {
157
fElementsToProcess = new ICElement[]{element};
162
* Adds the given delta to the collection of deltas
163
* that this operation has created. These deltas are
164
* automatically registered with the C Model Manager
165
* when the operation completes.
167
protected void addDelta(ICElementDelta delta) {
169
fDeltas = new LinkedList<ICElementDelta>();
174
* Registers the given reconcile delta with the C Model Manager.
176
protected void addReconcileDelta(IWorkingCopy workingCopy, ICElementDelta delta) {
177
HashMap<IWorkingCopy, ICElementDelta> reconcileDeltas = CModelManager.getDefault().reconcileDeltas;
178
CElementDelta previousDelta = (CElementDelta)reconcileDeltas.get(workingCopy);
179
if (previousDelta != null) {
180
ICElementDelta[] children = delta.getAffectedChildren();
181
for (ICElementDelta element : children) {
182
CElementDelta child = (CElementDelta)element;
183
previousDelta.insertDeltaTree(child.getElement(), child);
186
reconcileDeltas.put(workingCopy, delta);
191
* Deregister the reconcile delta for the given working copy
193
protected void removeReconcileDelta(IWorkingCopy workingCopy) {
194
CModelManager.getDefault().reconcileDeltas.remove(workingCopy);
198
* @see IProgressMonitor
200
public void beginTask(String name, int totalWork) {
201
if (fMonitor != null) {
202
fMonitor.beginTask(name, totalWork);
207
* Checks with the progress monitor to see whether this operation
208
* should be canceled. An operation should regularly call this method
209
* during its operation so that the user can cancel it.
211
* @exception OperationCanceledException if canceling the operation has been requested
212
* @see IProgressMonitor#isCanceled
214
protected void checkCanceled() {
216
throw new OperationCanceledException(CoreModelMessages.getString("operation.canceled")); //$NON-NLS-1$
221
* Common code used to verify the elements this operation is processing.
222
* @see CModelOperation#verify()
224
protected ICModelStatus commonVerify() {
225
if (fElementsToProcess == null || fElementsToProcess.length == 0) {
226
return new CModelStatus(ICModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
228
for (ICElement elementsToProces : fElementsToProcess) {
229
if (elementsToProces == null) {
230
return new CModelStatus(ICModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
233
return CModelStatus.VERIFIED_OK;
237
* Returns the translation unit the given element is contained in,
238
* or the element itself (if it is a compilation unit),
239
* otherwise <code>null</code>.
241
protected ITranslationUnit getTranslationUnitFor(ICElement element) {
242
if (element instanceof ITranslationUnit) {
243
return (ITranslationUnit)element;
244
} else if (element instanceof ISourceReference) {
245
ISourceReference ref = (ISourceReference)element;
246
return ref.getTranslationUnit();
252
* Convenience method to copy resources
254
protected void copyResources(IResource[] resources, IPath destinationPath) throws CModelException {
255
IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
256
IWorkspace workspace = resources[0].getWorkspace();
258
workspace.copy(resources, destinationPath, false, subProgressMonitor);
259
this.hasModifiedResource = true;
260
} catch (CoreException e) {
261
throw new CModelException(e);
266
* Convenience method to create a file
268
protected void createFile(IContainer folder, String name, InputStream contents, boolean force) throws CModelException {
269
IFile file= folder.getFile(new Path(name));
271
file.create(contents, force, getSubProgressMonitor(1));
272
this.hasModifiedResource = true;
273
} catch (CoreException e) {
274
throw new CModelException(e);
279
* Convenience method to create a folder
281
protected void createFolder(IContainer parentFolder, String name, boolean force) throws CModelException {
282
IFolder folder= parentFolder.getFolder(new Path(name));
284
// we should use true to create the file locally. Only VCM should use tru/false
285
folder.create(force, true, getSubProgressMonitor(1));
286
this.hasModifiedResource = true;
287
} catch (CoreException e) {
288
throw new CModelException(e);
293
* Convenience method to delete a resource
295
protected void deleteResource(IResource resource, boolean force) throws CModelException {
297
resource.delete(force, getSubProgressMonitor(1));
298
this.hasModifiedResource = true;
299
} catch (CoreException e) {
300
throw new CModelException(e);
305
* Convenience method to delete resources
307
protected void deleteResources(IResource[] resources, boolean force) throws CModelException {
308
if (resources == null || resources.length == 0) return;
309
IProgressMonitor subProgressMonitor = getSubProgressMonitor(resources.length);
310
IWorkspace workspace = resources[0].getWorkspace();
312
workspace.delete(resources, force, subProgressMonitor);
313
this.hasModifiedResource = true;
314
} catch (CoreException e) {
315
throw new CModelException(e);
320
* @see IProgressMonitor
323
if (fMonitor != null) {
329
* Verifies the operation can proceed and executes the operation.
330
* Subclasses should override <code>#verify</code> and
331
* <code>executeOperation</code> to implement the specific operation behavior.
333
* @exception CModelException The operation has failed.
335
protected void execute() throws CModelException {
336
ICModelStatus status= verify();
340
throw new CModelException(status);
345
* Convenience method to run an operation within this operation
347
public void executeNestedOperation(CModelOperation operation, int subWorkAmount) throws CModelException {
348
IProgressMonitor subProgressMonitor = getSubProgressMonitor(subWorkAmount);
349
// fix for 1FW7IKC, part (1)
351
operation.setNested(true);
352
operation.run(subProgressMonitor);
353
if (operation.hasModifiedResource()) {
354
this.hasModifiedResource = true;
356
//accumulate the nested operation deltas
357
if (operation.fDeltas != null) {
358
for (ICElementDelta delta : operation.fDeltas) {
362
} catch (CoreException ce) {
363
if (ce instanceof CModelException) {
364
throw (CModelException)ce;
366
// translate the core exception to a c model exception
367
if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
368
Throwable e = ce.getStatus().getException();
369
if (e instanceof CModelException) {
370
throw (CModelException) e;
373
throw new CModelException(ce);
378
* Performs the operation specific behavior. Subclasses must override.
380
protected abstract void executeOperation() throws CModelException;
383
* Returns the elements to which this operation applies,
384
* or <code>null</code> if not applicable.
386
protected ICElement[] getElementsToProcess() {
387
return fElementsToProcess;
391
* Returns the element to which this operation applies,
392
* or <code>null</code> if not applicable.
394
protected ICElement getElementToProcess() {
395
if (fElementsToProcess == null || fElementsToProcess.length == 0) {
398
return fElementsToProcess[0];
402
* Returns the C Model this operation is operating in.
404
public ICModel getCModel() {
405
if (fElementsToProcess == null || fElementsToProcess.length == 0) {
406
return getParentElement().getCModel();
408
return fElementsToProcess[0].getCModel();
412
* Returns the parent element to which this operation applies,
413
* or <code>null</code> if not applicable.
415
protected ICElement getParentElement() {
416
if (parentElements == null || parentElements.length == 0) {
419
return parentElements[0];
423
* Returns the parent elements to which this operation applies,
424
* or <code>null</code> if not applicable.
426
protected ICElement[] getParentElements() {
427
return parentElements;
431
* Returns the elements created by this operation.
433
public ICElement[] getResultElements() {
434
return fResultElements;
438
* Returns the scheduling rule for this operation (i.e. the resource that needs to be locked
439
* while this operation is running.
440
* Subclasses can override.
442
public ISchedulingRule getSchedulingRule() {
443
return ResourcesPlugin.getWorkspace().getRoot();
447
* Creates and returns a subprogress monitor if appropriate.
449
protected IProgressMonitor getSubProgressMonitor(int workAmount) {
450
IProgressMonitor sub = null;
451
if (fMonitor != null) {
452
sub = new SubProgressMonitor(fMonitor, workAmount, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
458
* Returns the <code>IWorkspace</code> this operation is working in, or
459
* <code>null</code> if this operation has no elements to process.
461
protected IWorkspace getWorkspace() {
462
if (fElementsToProcess != null && fElementsToProcess.length > 0) {
463
ICProject project = fElementsToProcess[0].getCProject();
464
if (project != null) {
465
return project.getCModel().getWorkspace();
472
* Returns whether this operation has performed any resource modifications.
473
* Returns false if this operation has not been executed yet.
475
public boolean hasModifiedResource() {
476
return !this.isReadOnly() && this.hasModifiedResource;
479
public void internalWorked(double work) {
480
if (fMonitor != null) {
481
fMonitor.internalWorked(work);
486
* @see IProgressMonitor
488
public boolean isCanceled() {
489
if (fMonitor != null) {
490
return fMonitor.isCanceled();
496
* Returns <code>true</code> if this operation performs no resource modifications,
497
* otherwise <code>false</code>. Subclasses must override.
499
public boolean isReadOnly() {
504
* Convenience method to move resources
506
protected void moveResources(IResource[] resources, IPath destinationPath) throws CModelException {
507
IProgressMonitor subProgressMonitor = null;
508
if (fMonitor != null) {
509
subProgressMonitor = new SubProgressMonitor(fMonitor, resources.length, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
511
IWorkspace workspace = resources[0].getWorkspace();
513
workspace.move(resources, destinationPath, false, subProgressMonitor);
514
this.hasModifiedResource = true;
515
} catch (CoreException e) {
516
throw new CModelException(e);
521
* Creates and returns a new <code>ICElementDelta</code>
524
public CElementDelta newCElementDelta() {
525
return new CElementDelta(getCModel());
529
* Registers any deltas this operation created, with the
532
protected void registerDeltas() {
533
if (fDeltas != null && !fNested) {
534
// hook to ensure working copies remain consistent
535
//makeWorkingCopiesConsistent(fDeltas);
536
CModelManager manager= CModelManager.getDefault();
537
for (ICElementDelta delta : fDeltas) {
538
manager.registerCModelDelta(delta);
544
* Returns the stack of operations running in the current thread.
545
* Returns an empty stack if no operations are currently running in this thread.
547
protected ArrayList<CModelOperation> getCurrentOperationStack() {
548
ArrayList<CModelOperation> stack = operationStacks.get();
550
stack = new ArrayList<CModelOperation>();
551
operationStacks.set(stack);
557
* Removes the last pushed operation from the stack of running operations.
558
* Returns the poped operation or null if the stack was empty.
560
protected CModelOperation popOperation() {
561
ArrayList<CModelOperation> stack = getCurrentOperationStack();
562
int size = stack.size();
564
if (size == 1) { // top level operation
565
operationStacks.set(null); // release reference (see http://bugs.eclipse.org/bugs/show_bug.cgi?id=33927)
567
return stack.remove(size-1);
574
* Pushes the given operation on the stack of operations currently running in this thread.
576
protected void pushOperation(CModelOperation operation) {
577
getCurrentOperationStack().add(operation);
581
* Returns whether this operation is the first operation to run in the current thread.
583
protected boolean isTopLevelOperation() {
584
ArrayList<CModelOperation> stack;
586
(stack = this.getCurrentOperationStack()).size() > 0
587
&& stack.get(0) == this;
591
* Main entry point for C Model operations. Executes this operation
592
* and registers any deltas created.
594
* @see IWorkspaceRunnable
595
* @exception CoreException if the operation fails
597
public void run(IProgressMonitor monitor) throws CoreException {
598
CModelManager manager= CModelManager.getDefault();
599
int previousDeltaCount = manager.fCModelDeltas.size();
607
// Fire if we change somethings
608
if (isTopLevelOperation()) {
609
if ((manager.fCModelDeltas.size() > previousDeltaCount || !manager.reconcileDeltas.isEmpty())
610
&& !this.hasModifiedResource()) {
611
manager.fire(ElementChangedEvent.POST_CHANGE);
621
* Main entry point for C Model operations. Runs a C Model Operation as an IWorkspaceRunnable
624
public void runOperation(IProgressMonitor monitor) throws CModelException {
625
ICModelStatus status = verify();
626
if (!status.isOK()) {
627
throw new CModelException(status);
633
// use IWorkspace.run(...) to ensure that a build will be done in autobuild mode
634
getCModel().getUnderlyingResource().getWorkspace()
635
.run(this, getSchedulingRule(), IWorkspace.AVOID_UPDATE, monitor);
637
} catch (CoreException ce) {
638
if (ce instanceof CModelException) {
639
throw (CModelException)ce;
640
} else if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
641
Throwable e = ce.getStatus().getException();
642
if (e instanceof CModelException) {
643
throw (CModelException)e;
646
throw new CModelException(ce);
652
* @see IProgressMonitor
654
public void setCanceled(boolean b) {
655
if (fMonitor != null) {
656
fMonitor.setCanceled(b);
661
* Sets whether this operation is nested or not.
662
* @see CreateElementInTUOperation#checkCanceled
664
protected void setNested(boolean nested) {
669
* @see IProgressMonitor
671
public void setTaskName(String name) {
672
if (fMonitor != null) {
673
fMonitor.setTaskName(name);
678
* @see IProgressMonitor
680
public void subTask(String name) {
681
if (fMonitor != null) {
682
fMonitor.subTask(name);
686
* Returns a status indicating if there is any known reason
687
* this operation will fail. Operations are verified before they
690
* Subclasses must override if they have any conditions to verify
691
* before this operation executes.
695
protected ICModelStatus verify() {
696
return commonVerify();
700
* @see IProgressMonitor
702
public void worked(int work) {
703
if (fMonitor != null) {
704
fMonitor.worked(work);