Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: b937a390e3de95491a790cac2a56044b65777991 (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
package org.eclipse.papyrus.req.reqif.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.util.FeatureMapUtil;
import org.eclipse.emf.ecore.util.InternalEList;

/**
 * A mapping building traverser of a collection of {@link EObject#eAllContents content trees};
 * the map is from {@link EObject} to <code>EObject</code>, i.e., from original to copy;
 * use {@link EcoreUtil#copy EcoreUtil.copy} or {@link EcoreUtil#copyAll EcoreUtil.copyAll} to do routine copies.
 * Since this implementation extends a Map implementation, it acts as the result of the over all copy.
 * The client can call {@link #copy copy} and {@link #copyAll copyAll} repeatedly.
 * When all the objects have been copied,
 * the client should call {@link #copyReferences copyReferences}
 * to copy the {@link #copyReference appropriate} {@link EObject#eCrossReferences cross references}.
 *<pre>
 *  Copier copier = new Copier();
 *  EObject result = copier.copy(eObject);
 *  Collection results = copier.copyAll(eObjects);
 *  copier.copyReferences();
 *</pre>
 * The copier delegates to {@link #copyContainment copyContainment}, {@link #copyAttribute copyAttribute} during the copy phase
 * and to {@link #copyReference copyReference}, during the cross reference phase.
 * This allows tailored handling through derivation.
 */
public  class CopierWithoutContainment extends LinkedHashMap<EObject, EObject>
{
	private static final long serialVersionUID = 1L;

	/**
	 * Whether proxies should be resolved during copying.
	 */
	protected boolean resolveProxies = true;

	/**
	 * Whether non-copied references should be used during copying.
	 */
	protected boolean useOriginalReferences = true;

	/**
	 * Creates an instance.
	 */
	public CopierWithoutContainment()
	{
		super();
	}

	/**
	 * Creates an instance that resolves proxies or not as specified.
	 * @param resolveProxies whether proxies should be resolved while copying.
	 */
	public CopierWithoutContainment(boolean resolveProxies)
	{
		this.resolveProxies = resolveProxies;
	}

	/**
	 * Creates an instance that resolves proxies or not and uses non-copied references or not as specified.
	 * @param resolveProxies whether proxies should be resolved while copying.
	 * @param useOriginalReferences whether non-copied references should be used while copying.
	 */
	public CopierWithoutContainment(boolean resolveProxies, boolean useOriginalReferences)
	{
		this.resolveProxies = resolveProxies;
		this.useOriginalReferences = useOriginalReferences;
	}

	/**
	 * Returns a collection containing a copy of each EObject in the given collection.
	 * @param eObjects the collection of objects to copy.
	 * @return the collection of copies.
	 */
	public <T> Collection<T> copyAll(Collection<? extends T> eObjects)
	{
		Collection<T> result = new ArrayList<T>(eObjects.size());
		for (Object object : eObjects)
		{
			@SuppressWarnings("unchecked") T t = (T)copy((EObject)object);
			if (t != null)
			{
				result.add(t);
			}
		}
		return result;
	}

	/**
	 * Returns a copy of the given eObject.
	 * @param eObject the object to copy.
	 * @return the copy.
	 */
	public EObject copy(EObject eObject)
	{
		if (eObject == null)
		{
			return null;
		}
		else
		{
			EObject copyEObject = createCopy(eObject);
			if (copyEObject != null)
			{
				put(eObject, copyEObject);
				EClass eClass = eObject.eClass();
				for (int i = 0, size = eClass.getFeatureCount(); i < size; ++i)
				{
					EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(i);
					if (eStructuralFeature.isChangeable() && !eStructuralFeature.isDerived())
					{
						if (eStructuralFeature instanceof EAttribute)
						{
							copyAttribute((EAttribute)eStructuralFeature, eObject, copyEObject);
						}
						else
						{
							EReference eReference = (EReference)eStructuralFeature;
							if (eReference.isContainment())
							{
								//copyContainment(eReference, eObject, copyEObject);
							}
						}
					}
				}

				copyProxyURI(eObject, copyEObject);
			}

			return copyEObject;
		}
	}

	/**
	 * Copies the proxy URI from the original to the copy, if present.
	 * @param eObject the object being copied.
	 * @param copyEObject the copy being initialized.
	 */
	protected void copyProxyURI(EObject eObject, EObject copyEObject)
	{
		if (eObject.eIsProxy())
		{
			((InternalEObject)copyEObject).eSetProxyURI(((InternalEObject)eObject).eProxyURI());
		}
	}

	/**
	 * Returns a new instance of the object's target class.
	 * @param eObject the object to copy.
	 * @return a new instance of the target class.
	 * @see #getTarget(EObject)
	 * @see EcoreUtil#create(EClass)
	 */
	protected EObject createCopy(EObject eObject)
	{
		EClass eClass = getTarget(eObject);
		return eClass == null ? null : EcoreUtil.create(eClass);
	}

	/**
	 * Returns the target class used to create a copy instance for the given instance object.
	 * @param eObject the object to be copied.
	 * @return the target class used to create a copy instance.
	 * @since 2.10
	 */
	protected EClass getTarget(EObject eObject)
	{
		return getTarget(eObject.eClass());
	}

	/**
	 * Returns the target class used to create a copy instance for objects of the given source class.
	 * @param eClass the source class.
	 * @return the target class used to create a copy instance.
	 * @see #getTarget(EStructuralFeature, EObject, EObject)
	 */
	protected EClass getTarget(EClass eClass)
	{
		return eClass;
	}

	/**
	 * Returns a setting for the feature and copy instance to be populated with the original object's source feature's value.
	 * @param eStructuralFeature the source feature.
	 * @return the target feature used to populate a copy instance.
	 * @see #getTarget(EStructuralFeature)
	 * @see #getTarget(EObject)
	 * @since 2.10
	 */
	protected EStructuralFeature.Setting getTarget(EStructuralFeature eStructuralFeature, EObject eObject, EObject copyEObject)
	{
		EStructuralFeature targetEStructuralFeature = getTarget(eStructuralFeature);
		return targetEStructuralFeature == null ? null : ((InternalEObject)copyEObject).eSetting(targetEStructuralFeature);
	}

	/**
	 * Returns the target feature used to populate a copy instance from the given source feature.
	 * @param eStructuralFeature the source feature.
	 * @return the target feature used to populate a copy instance.
	 * @see #getTarget(EClass)
	 */
	protected EStructuralFeature getTarget(EStructuralFeature eStructuralFeature)
	{
		return eStructuralFeature;
	}

	/**
	 * Called to handle the copying of a containment feature;
	 * this adds a list of copies or sets a single copy as appropriate for the multiplicity.
	 * @param eReference the feature to copy.
	 * @param eObject the object from which to copy.
	 * @param copyEObject the object to copy to.
	 */
	protected void copyContainment(EReference eReference, EObject eObject, EObject copyEObject)
	{
		if (eObject.eIsSet(eReference))
		{
			EStructuralFeature.Setting setting = getTarget(eReference, eObject, copyEObject);
			if (setting != null)
			{
				Object value = eObject.eGet(eReference);
				if (eReference.isMany())
				{
					@SuppressWarnings("unchecked")
					List<EObject> target = (List<EObject>)value;
					setting.set(copyAll(target));
				}
				else
				{
					setting.set(copy((EObject)value));
				}
			}
		}
	}

	/**
	 * Called to handle the copying of an attribute;
	 * this adds a list of values or sets a single value as appropriate for the multiplicity.
	 * @param eAttribute the attribute to copy.
	 * @param eObject the object from which to copy.
	 * @param copyEObject the object to copy to.
	 */
	protected void copyAttribute(EAttribute eAttribute, EObject eObject, EObject copyEObject)
	{
		if (eObject.eIsSet(eAttribute))
		{
			if (FeatureMapUtil.isFeatureMap(eAttribute))
			{
				FeatureMap featureMap = (FeatureMap)eObject.eGet(eAttribute);
				copyFeatureMap(featureMap);
			}
			else
			{
				EStructuralFeature.Setting setting = getTarget(eAttribute, eObject, copyEObject);
				if (setting != null)
				{
					copyAttributeValue(eAttribute, eObject, eObject.eGet(eAttribute), setting);
				}
			}
		}
	}

	/**
	 * Call to handle copying the contained objects within a feature map.
	 * @param featureMap the feature map the copy.
	 * @since 2.10
	 */
	protected void copyFeatureMap(FeatureMap featureMap)
	{
		for (int i = 0, size = featureMap.size(); i < size; ++i)
		{
			EStructuralFeature feature = featureMap.getEStructuralFeature(i);
			if (feature instanceof EReference && ((EReference)feature).isContainment())
			{
				Object value = featureMap.getValue(i);
				if (value != null)
				{
					// The containment references are hooked up later during copyReferences.
					//
					copy((EObject)value);
				}
			}
		}
	}

	/**
	 * Called to handle copying of an attribute's value to the target setting.
	 * @param eAttribute the attribute of the source object corresponding to the value.
	 * @param eObject the object being copied.
	 * @param value the value to be copied.
	 * @param setting the feature-value pair that is the target of of the copy.
	 * @since 2.10
	 */
	protected void copyAttributeValue(EAttribute eAttribute, EObject eObject, Object value, EStructuralFeature.Setting setting)
	{
		setting.set(value);
	}

	/**
	 * Hooks up cross references; it delegates to {@link #copyReference copyReference}.
	 */
	public void copyReferences()
	{
		for (Map.Entry<EObject, EObject> entry  : entrySet())
		{
			EObject eObject = entry.getKey();
			EObject copyEObject = entry.getValue();
			EClass eClass = eObject.eClass();
			for (int j = 0, size = eClass.getFeatureCount(); j < size; ++j)
			{
				EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(j);
				if (eStructuralFeature.isChangeable() && !eStructuralFeature.isDerived())
				{
					if (eStructuralFeature instanceof EReference)
					{
						EReference eReference = (EReference)eStructuralFeature;
						if (!eReference.isContainment() && !eReference.isContainer())
						{
							copyReference(eReference, eObject, copyEObject);
						}
					}
					else if (FeatureMapUtil.isFeatureMap(eStructuralFeature))
					{
						FeatureMap copyFeatureMap = (FeatureMap)getTarget(eStructuralFeature, eObject, copyEObject);
						if (copyFeatureMap != null)
						{
							FeatureMap featureMap = (FeatureMap)eObject.eGet(eStructuralFeature);
							int copyFeatureMapSize = copyFeatureMap.size();
							for (int k = 0, featureMapSize = featureMap.size(); k < featureMapSize; ++k)
							{
								EStructuralFeature feature = featureMap.getEStructuralFeature(k);
								if (feature instanceof EReference)
								{
									Object referencedEObject = featureMap.getValue(k);
									Object copyReferencedEObject = get(referencedEObject);
									if (copyReferencedEObject == null && referencedEObject != null)
									{
										EReference reference = (EReference)feature;
										if (!useOriginalReferences || reference.isContainment() || reference.getEOpposite() != null)
										{
											continue;
										}
										copyReferencedEObject = referencedEObject;
									}

									// If we can't add it, it must already be in the list so find it and move it to the end.
									//
									if (!copyFeatureMap.add(feature, copyReferencedEObject))
									{
										for (int l = 0; l < copyFeatureMapSize; ++l)
										{
											if (copyFeatureMap.getEStructuralFeature(l) == feature && copyFeatureMap.getValue(l) == copyReferencedEObject)
											{
												copyFeatureMap.move(copyFeatureMap.size() - 1, l);
												--copyFeatureMapSize;
												break;
											}
										}
									}
								}
								else
								{
									copyFeatureMap.add(getTarget(featureMap.getEStructuralFeature(k)), featureMap.getValue(k));
								}
							}
						}
					}
				}
			}
		}
	}

	/**
	 * Called to handle the copying of a cross reference;
	 * this adds values or sets a single value as appropriate for the multiplicity
	 * while omitting any bidirectional reference that isn't in the copy map.
	 * @param eReference the reference to copy.
	 * @param eObject the object from which to copy.
	 * @param copyEObject the object to copy to.
	 */
	protected void copyReference(EReference eReference, EObject eObject, EObject copyEObject)
	{
		if (eObject.eIsSet(eReference))
		{
			EStructuralFeature.Setting setting = getTarget(eReference, eObject, copyEObject);
			if (setting != null)
			{
				Object value = eObject.eGet(eReference, resolveProxies);
				if (eReference.isMany())
				{
					@SuppressWarnings("unchecked") InternalEList<EObject> source = (InternalEList<EObject>)value;
					@SuppressWarnings("unchecked") InternalEList<EObject> target = (InternalEList<EObject>)setting;
					if (source.isEmpty())
					{
						target.clear();
					}
					else
					{
						boolean isBidirectional = eReference.getEOpposite() != null;
						int index = 0;
						for (Iterator<EObject> k = resolveProxies ? source.iterator() : source.basicIterator(); k.hasNext();)
						{
							EObject referencedEObject = k.next();
							EObject copyReferencedEObject = get(referencedEObject);
							if (copyReferencedEObject == null)
							{
								if (useOriginalReferences && !isBidirectional)
								{
									target.addUnique(index, referencedEObject);
									++index;
								}
							}
							else
							{
								if (isBidirectional)
								{
									int position = target.indexOf(copyReferencedEObject);
									if (position == -1)
									{
										target.addUnique(index, copyReferencedEObject);
									}
									else if (index != position)
									{
										target.move(index, copyReferencedEObject);
									}
								}
								else
								{
									target.addUnique(index, copyReferencedEObject);
								}
								++index;
							}
						}
					}
				}
				else
				{
					if (value == null)
					{
						setting.set(null);
					}
					else
					{
						Object copyReferencedEObject = get(value);
						if (copyReferencedEObject == null)
						{
							if (useOriginalReferences && eReference.getEOpposite() == null)
							{
								setting.set(value);
							}
						}
						else
						{
							setting.set(copyReferencedEObject);
						}
					}
				}
			}
		}
	}
}

Back to the top