Skip to main content
summaryrefslogtreecommitdiffstats
blob: ae66505e97c922e5741f03430e98a90eb0b1e112 (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
package org.eclipse.cdt.internal.core.model;

/*
 * (c) Copyright IBM Corp. 2000, 2001.
 * All Rights Reserved.
 */

import java.util.HashMap;
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.ICModelStatus;
import org.eclipse.cdt.core.model.ICModelStatusConstants;

/**
 * This class is used to perform operations on multiple <code>ICElement</code>.
 * It is responible for running each operation in turn, collecting
 * the errors and merging the corresponding <code>CElementDelta</code>s.
 * <p>
 * If several errors occured, they are collected in a multi-status
 * <code>CModelStatus</code>. Otherwise, a simple <code>CModelStatus</code>
 * is thrown.
 */
public abstract class MultiOperation extends CModelOperation {
	/**
	 * The list of renamings supplied to the operation
	 */
	protected String[] fRenamingsList= null;

	/**
	 * Table specifying the new parent for elements being 
	 * copied/moved/renamed.
	 * Keyed by elements being processed, and
	 * values are the corresponding destination parent.
	 */
	protected Map fParentElements;

	/**
	 * Table specifying insertion positions for elements being 
	 * copied/moved/renamed. Keyed by elements being processed, and
	 * values are the corresponding insertion point.
	 * @see processElements(IProgressMonitor)
	 */
	protected Map fInsertBeforeElements= new HashMap(1);

	/**
	 * This table presents the data in <code>fRenamingList</code> in a more
	 * convenient way.
	 */
	protected Map fRenamings;

	/**
	 * Creates a new <code>MultiOperation</code>.
	 */
	protected MultiOperation(ICElement[] elementsToProcess, ICElement[] parentElements, boolean force) {
		super(elementsToProcess, parentElements, force);
		fParentElements = new HashMap(elementsToProcess.length);
		if (elementsToProcess.length == parentElements.length) {
			for (int i = 0; i < elementsToProcess.length; i++) {
				fParentElements.put(elementsToProcess[i], parentElements[i]);
			}
		} else { //same destination for all elements to be moved/copied/renamed
			for (int i = 0; i < elementsToProcess.length; i++) {
				fParentElements.put(elementsToProcess[i], parentElements[0]);
			}
		}
	}

	/**
	 * Creates a new <code>MultiOperation</code> on <code>elementsToProcess</code>.
	 */
	protected MultiOperation(ICElement[] elementsToProcess, boolean force) {
		super(elementsToProcess, force);
	}

	/**
	 * Convenience method to create a <code>CModelException</code>
	 * embending a <code>CModelStatus</code>.
	 */
	protected void error(int code, ICElement element) throws CModelException {
		throw new CModelException(new CModelStatus(code, element));
	}

	/**
	 * Executes the operation.
	 *
	 * @exception CModelException if one or several errors occured during the operation.
	 * If multiple errors occured, the corresponding <code>CModelStatus</code> is a
	 * multi-status. Otherwise, it is a simple one.
	 */
	protected void executeOperation() throws CModelException {
		try {
			processElements();
		} catch (CModelException cme) {
			throw cme;
		} finally {
			mergeDeltas();
		}
	}

	/**
	 * Returns the parent of the element being copied/moved/renamed.
	 */
	protected ICElement getDestinationParent(ICElement child) {
		return (ICElement)fParentElements.get(child);
	}

	/**
	 * Returns the name to be used by the progress monitor.
	 */
	protected abstract String getMainTaskName();

	/**
	 * Returns the new name for <code>element</code>, or <code>null</code>
	 * if there are no renamings specified.
	 */
	protected String getNewNameFor(ICElement element) {
		if (fRenamings != null)
			return (String) fRenamings.get(element);
		else
			return null;
	}

	/**
	 * Sets up the renamings hashtable - keys are the elements and
	 * values are the new name.
	 */
	private void initializeRenamings() {
		if (fRenamingsList != null && fRenamingsList.length == fElementsToProcess.length) {
			fRenamings = new HashMap(fRenamingsList.length);
			for (int i = 0; i < fRenamingsList.length; i++) {
				if (fRenamingsList[i] != null) {
					fRenamings.put(fElementsToProcess[i], fRenamingsList[i]);
				}
			}
		}
	}

	/**
	 * Returns <code>true</code> if this operation represents a move or rename, <code>false</code>
	 * if this operation represents a copy.<br>
	 * Note: a rename is just a move within the same parent with a name change.
	 */
	protected boolean isMove() {
		return false;
	}

	/**
	 * Returns <code>true</code> if this operation represents a rename, <code>false</code>
	 * if this operation represents a copy or move.
	 */
	protected boolean isRename() {
		return false;
	}

	/**
	 * Process all of the changed deltas generated by these operations.
	 */
	protected void mergeDeltas() {
		if (fDeltas != null) {
			CElementDelta rootDelta = newCElementDelta();
			boolean insertedTree = false;
			for (int i = 0; i < fDeltas.length; i++) {
				ICElementDelta delta = fDeltas[i];
				ICElementDelta[] children = delta.getAffectedChildren();
				for (int j = 0; j < children.length; j++) {
					CElementDelta projectDelta = (CElementDelta) children[j];
					rootDelta.insertDeltaTree(projectDelta.getElement(), projectDelta);
					insertedTree = true;
				}
			}
			if (insertedTree)
				fDeltas = new ICElementDelta[] {rootDelta};
			else
				fDeltas = null;
		}
	}

	/**
	 * Subclasses must implement this method to process a given <code>ICElement</code>.
	 */
	protected abstract void processElement(ICElement element) throws CModelException;

	/**
	 * Processes all the <code>ICElement</code>s in turn, collecting errors
	 * and updating the progress monitor.
	 *
	 * @exception CModelException if one or several operation(s) was unable to
	 * be completed.
	 */
	protected void processElements() throws CModelException {
		beginTask(getMainTaskName(), fElementsToProcess.length);
		ICModelStatus[] errors = new ICModelStatus[3];
		int errorsCounter = 0;
		for (int i = 0; i < fElementsToProcess.length; i++) {
			try {
				verify(fElementsToProcess[i]);
				processElement(fElementsToProcess[i]);
			} catch (CModelException jme) {
				if (errorsCounter == errors.length) {
					// resize
					System.arraycopy(errors, 0, (errors = new ICModelStatus[errorsCounter*2]), 0, errorsCounter);
				}
				errors[errorsCounter++] = jme.getCModelStatus();
			} finally {
				worked(1);
			}
		}
		done();
		if (errorsCounter == 1) {
			throw new CModelException(errors[0]);
		} else if (errorsCounter > 1) {
			if (errorsCounter != errors.length) {
				// resize
				System.arraycopy(errors, 0, (errors = new ICModelStatus[errorsCounter]), 0, errorsCounter);
			}
			throw new CModelException(CModelStatus.newMultiStatus(errors));
			}
	}

	/**
	 * Sets the insertion position in the new container for the modified element. The element
	 * being modified will be inserted before the specified new sibling. The given sibling
	 * must be a child of the destination container specified for the modified element.
	 * The default is <code>null</code>, which indicates that the element is to be
	 * inserted at the end of the container.
	 */
	public void setInsertBefore(ICElement modifiedElement, ICElement newSibling) {
		fInsertBeforeElements.put(modifiedElement, newSibling);
	}

	/**
	 * Sets the new names to use for each element being copied. The renamings
	 * correspond to the elements being processed, and the number of
	 * renamings must match the number of elements being processed.
	 * A <code>null</code> entry in the list indicates that an element
	 * is not to be renamed.
	 *
	 * <p>Note that some renamings may not be used.  If both a parent
	 * and a child have been selected for copy/move, only the parent
	 * is changed.  Therefore, if a new name is specified for the child,
	 * the child's name will not be changed.
	 */
	public void setRenamings(String[] renamings) {
		fRenamingsList = renamings;
		initializeRenamings();
	}

	/**
	 * This method is called for each <code>ICElement</code> before
	 * <code>processElement</code>. It should check that this <code>element</code>
	 * can be processed.
	 */
	protected abstract void verify(ICElement element) throws CModelException;

	/**
	 * Verifies that the <code>destination</code> specified for the <code>element</code> is valid for the types of the
	 * <code>element</code> and <code>destination</code>.
	 */
	protected void verifyDestination(ICElement element, ICElement destination) throws CModelException {
		if (destination == null || !destination.exists())
			error(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST, destination);
	
	}

	/**
	 * Verify that the new name specified for <code>element</code> is
	 * valid for that type of C element.
	 */
	protected void verifyRenaming(ICElement element) throws CModelException {
		String newName = getNewNameFor(element);
		boolean isValid = true;
		// Validate the name here.
		if (newName.indexOf(' ') != -1) {
			isValid = false;
		}

		if (!isValid) {
			throw new CModelException(new CModelStatus(ICModelStatusConstants.INVALID_NAME, element, newName));
		}
	}

	/**
	 * Verifies that the positioning sibling specified for the <code>element</code> is exists and
	 * its parent is the destination container of this <code>element</code>.
	 */
	protected void verifySibling(ICElement element, ICElement destination) throws CModelException {
		ICElement insertBeforeElement = (ICElement) fInsertBeforeElements.get(element);
		if (insertBeforeElement != null) {
			if (!insertBeforeElement.exists() || !insertBeforeElement.getParent().equals(destination)) {
				error(ICModelStatusConstants.INVALID_SIBLING, insertBeforeElement);
			}
		}
	}
}

Back to the top