Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 161169
Collapse All | Expand All

(-)src/org/eclipse/emf/transaction/tests/TransactionChangeRecorderTest.java (-1 / +213 lines)
Lines 31-38 Link Here
31
import org.eclipse.emf.ecore.EPackage;
31
import org.eclipse.emf.ecore.EPackage;
32
import org.eclipse.emf.ecore.resource.Resource;
32
import org.eclipse.emf.ecore.resource.Resource;
33
import org.eclipse.emf.ecore.resource.ResourceSet;
33
import org.eclipse.emf.ecore.resource.ResourceSet;
34
import org.eclipse.emf.ecore.util.EcoreUtil;
34
import org.eclipse.emf.ecore.util.InternalEList;
35
import org.eclipse.emf.ecore.util.InternalEList;
35
import org.eclipse.emf.transaction.impl.TransactionChangeRecorder;
36
import org.eclipse.emf.transaction.impl.TransactionChangeRecorder;
37
import org.eclipse.emf.transaction.util.TransactionUtil;
36
38
37
39
38
/**
40
/**
Lines 151-157 Link Here
151
	
153
	
152
	/**
154
	/**
153
	 * Tests that the change recorder is correctly propagated to the objects in
155
	 * Tests that the change recorder is correctly propagated to the objects in
154
	 * a resource when it is loadedwhen loading in a read-write transaction
156
	 * a resource when it is loaded when loading in a read-write transaction
155
	 * (creating change descriptions).
157
	 * (creating change descriptions).
156
	 */
158
	 */
157
	public void test_changeRecorderPropagatedOnLoad_writeTX() {
159
	public void test_changeRecorderPropagatedOnLoad_writeTX() {
Lines 183-188 Link Here
183
		assertFalse(nestedResource2.isLoaded());
185
		assertFalse(nestedResource2.isLoaded());
184
	}
186
	}
185
	
187
	
188
	/**
189
	 * Tests that disposing an editing domain (and its change recorder) removes
190
	 * the change recorder from all of the current contents of the resource
191
	 * set.
192
	 */
193
	public void test_changeRecorderDispose_161169() {
194
		startWriting();
195
		loadRoot();
196
		
197
		TransactionChangeRecorder recorder = getRecorder(rootResource);
198
		
199
		EClass eclass = findClass("root/A", true); //$NON-NLS-1$
200
		
201
		assertSame(recorder, getRecorder(eclass));
202
		
203
		commit();
204
		
205
		domain.dispose();
206
		
207
		try {
208
			// attempt to change it without a transaction.  Should be allowed
209
			eclass.setName("NewName"); //$NON-NLS-1$
210
		} catch (Exception e) {
211
			e.printStackTrace();
212
			fail("Should not have asserted the transaction protocol: " //$NON-NLS-1$
213
					+ e.getLocalizedMessage());
214
		}
215
	}
216
	
217
	/**
218
	 * Tests that the change recorder is implicitly removed even from model
219
	 * elements that were not attached to the resource set at the time when
220
	 * the editing domain was disposed.
221
	 */
222
	public void test_changeRecorderDispose_detachedElements_161169() {
223
		startWriting();
224
		loadRoot();
225
		
226
		TransactionChangeRecorder recorder = getRecorder(rootResource);
227
		
228
		EClass eclass = findClass("root/A", true); //$NON-NLS-1$
229
		
230
		assertSame(recorder, getRecorder(eclass));
231
		
232
		// detach the EClass
233
		eclass.getEPackage().getEClassifiers().remove(eclass);
234
		
235
		commit();
236
		
237
		domain.dispose();
238
		
239
		try {
240
			// attempt to change it without a transaction.  Should be allowed
241
			eclass.setName("NewName"); //$NON-NLS-1$
242
		} catch (Exception e) {
243
			e.printStackTrace();
244
			fail("Should not have asserted the transaction protocol: " //$NON-NLS-1$
245
					+ e.getLocalizedMessage());
246
		}
247
	}
248
	
249
	/**
250
	 * Tests the new utility for "freeing" resources from the transactional
251
	 * protocol of an editing domain.
252
	 */
253
	public void test_freeDetachedResources_161169() {
254
		startWriting();
255
		loadRoot();
256
		
257
		TransactionChangeRecorder recorder = getRecorder(rootResource);
258
		
259
		EClass eclass = findClass("root/A", true); //$NON-NLS-1$
260
		
261
		assertSame(recorder, getRecorder(eclass));
262
		
263
		commit();
264
		
265
		// no transaction protocol on this
266
		domain.getResourceSet().getResources().remove(rootResource);
267
		
268
		// set the resource free
269
		TransactionUtil.disconnectFromEditingDomain(rootResource);
270
		
271
		try {
272
			// attempt to change it without a transaction.  Should be allowed
273
			eclass.setName("NewName"); //$NON-NLS-1$
274
		} catch (Exception e) {
275
			e.printStackTrace();
276
			fail("Should not have asserted the transaction protocol: " //$NON-NLS-1$
277
					+ e.getLocalizedMessage());
278
		}
279
	}
280
	
281
	/**
282
	 * Tests that the new utility frees only the proper contents of the resource.
283
	 */
284
	public void test_freeDetachedResources_properContents_161169() {
285
		startWriting();
286
		loadRoot();
287
		
288
		TransactionChangeRecorder recorder = getRecorder(rootResource);
289
		
290
		EClass eclass = findClass("root/A", true); //$NON-NLS-1$
291
		EClass nested = findClass("root/nested1/nested2/nested3/D", true); //$NON-NLS-1$
292
		
293
		assertSame(recorder, getRecorder(eclass));
294
		
295
		commit();
296
		
297
		// no transaction protocol on this
298
		domain.getResourceSet().getResources().remove(rootResource);
299
		
300
		// set the root resource free
301
		TransactionUtil.disconnectFromEditingDomain(rootResource);
302
		
303
		try {
304
			// set the nested resource free
305
			TransactionUtil.disconnectFromEditingDomain(nestedResource1);
306
			
307
			fail("Should have thrown IllegalArgumentException"); //$NON-NLS-1$
308
		} catch (IllegalArgumentException e) {
309
			// pass
310
			System.out.println("Got expected exception: " + e.getLocalizedMessage()); //$NON-NLS-1$
311
		}
312
		
313
		try {
314
			// modify the nested element
315
			nested.setName("NewName"); //$NON-NLS-1$
316
			
317
			fail("Should have thrown IllegalStateException"); //$NON-NLS-1$
318
		} catch (IllegalStateException e) {
319
			// pass
320
			System.out.println("Got expected exception: " + e.getLocalizedMessage()); //$NON-NLS-1$
321
		}
322
	}
323
	
324
	/**
325
	 * Tests the new utility for "freeing" elements from the transactional
326
	 * protocol of an editing domain.
327
	 */
328
	public void test_freeDetachedElements_161169() {
329
		startWriting();
330
		loadRoot();
331
		
332
		TransactionChangeRecorder recorder = getRecorder(rootResource);
333
		
334
		EClass eclass = findClass("root/A", true); //$NON-NLS-1$
335
		
336
		assertSame(recorder, getRecorder(eclass));
337
		
338
		// detach some ancestor, just for fun
339
		EObject container = eclass.eContainer();
340
		EcoreUtil.remove(container);
341
		
342
		commit();
343
		
344
		// set the element free
345
		TransactionUtil.disconnectFromEditingDomain(container);
346
		
347
		try {
348
			// attempt to change it without a transaction.  Should be allowed
349
			eclass.setName("NewName"); //$NON-NLS-1$
350
		} catch (Exception e) {
351
			e.printStackTrace();
352
			fail("Should not have asserted the transaction protocol: " //$NON-NLS-1$
353
					+ e.getLocalizedMessage());
354
		}
355
	}
356
	
357
	/**
358
	 * Tests that the new utility frees only the proper contents of an element.
359
	 */
360
	public void test_freeDetachedElements_properContents_161169() {
361
		startWriting();
362
		loadRoot();
363
		
364
		TransactionChangeRecorder recorder = getRecorder(rootResource);
365
		
366
		EClass eclass = findClass("root/A", true); //$NON-NLS-1$
367
		EClass nested = findClass("root/nested1/nested2/nested3/D", true); //$NON-NLS-1$
368
		
369
		assertSame(recorder, getRecorder(eclass));
370
		
371
		// detach some ancestor, just for fun
372
		EObject container = eclass.eContainer();
373
		EcoreUtil.remove(container);
374
 		
375
		commit();
376
		
377
		try {
378
			// set the nested element free
379
			TransactionUtil.disconnectFromEditingDomain(nested);
380
			
381
			fail("Should have thrown IllegalArgumentException"); //$NON-NLS-1$
382
		} catch (IllegalArgumentException e) {
383
			// pass
384
			System.out.println("Got expected exception: " + e.getLocalizedMessage()); //$NON-NLS-1$
385
		}
386
		
387
		try {
388
			// modify the nested element
389
			nested.setName("NewName"); //$NON-NLS-1$
390
			
391
			fail("Should have thrown IllegalStateException"); //$NON-NLS-1$
392
		} catch (IllegalStateException e) {
393
			// pass
394
			System.out.println("Got expected exception: " + e.getLocalizedMessage()); //$NON-NLS-1$
395
		}
396
	}
397
	
186
	//
398
	//
187
	// Framework methods
399
	// Framework methods
188
	//
400
	//
(-)src/org/eclipse/emf/transaction/util/TransactionUtil.java (+113 lines)
Lines 16-28 Link Here
16
 */
16
 */
17
package org.eclipse.emf.transaction.util;
17
package org.eclipse.emf.transaction.util;
18
18
19
import java.util.Collections;
20
import java.util.Iterator;
21
22
import org.eclipse.emf.common.notify.Notifier;
19
import org.eclipse.emf.ecore.EObject;
23
import org.eclipse.emf.ecore.EObject;
20
import org.eclipse.emf.ecore.resource.Resource;
24
import org.eclipse.emf.ecore.resource.Resource;
21
import org.eclipse.emf.ecore.resource.ResourceSet;
25
import org.eclipse.emf.ecore.resource.ResourceSet;
26
import org.eclipse.emf.ecore.util.EcoreUtil;
22
import org.eclipse.emf.edit.domain.EditingDomain;
27
import org.eclipse.emf.edit.domain.EditingDomain;
23
import org.eclipse.emf.edit.domain.IEditingDomainProvider;
28
import org.eclipse.emf.edit.domain.IEditingDomainProvider;
24
import org.eclipse.emf.transaction.Transaction;
29
import org.eclipse.emf.transaction.Transaction;
25
import org.eclipse.emf.transaction.TransactionalEditingDomain;
30
import org.eclipse.emf.transaction.TransactionalEditingDomain;
31
import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain;
32
import org.eclipse.emf.transaction.impl.TransactionChangeRecorder;
26
33
27
/**
34
/**
28
 * Static utilities for dealing with EMF elements and resources in a
35
 * Static utilities for dealing with EMF elements and resources in a
Lines 125-128 Link Here
125
		
132
		
126
		return result;
133
		return result;
127
	}
134
	}
135
	
136
	/**
137
	 * <p>
138
	 * Disconnects the specified resource from its editing domain, so that it is
139
	 * released from the constraints of the transactional environment.  Note
140
	 * that this is only permitted if the resource is not currently attached to
141
	 * the resource set of the editing domain in question.
142
	 * </p><p>
143
	 * This should only be done with extreme caution.  If any other resources
144
	 * that are still being managed by the transactional editing domain have
145
	 * dependencies on this <tt>resource</tt>, then existing undo/redo
146
	 * information for these may be corrupted and future undo recording may not
147
	 * be complete.
148
	 * </p>
149
	 * 
150
	 * @param resource a resource to disconnect from its current editing domain,
151
	 *     if any
152
	 * 
153
	 * @throws IllegalStateException if the specified <tt>resource</tt> is
154
	 *     still in the {@link ResourceSet} managed by its current editing
155
	 *     domain
156
	 * 
157
	 * @since 1.1
158
	 */
159
	public static void disconnectFromEditingDomain(Resource resource) {
160
		disconnectFromEditingDomain0(resource);
161
	}
162
	
163
	/**
164
	 * <p>
165
	 * Disconnects the specified element from its editing domain, so that it is
166
	 * released from the constraints of the transactional environment.  Note
167
	 * that this is only permitted if the element is not currently attached to
168
	 * the resource set of the editing domain in question.
169
	 * </p><p>
170
	 * This should only be done with extreme caution.  If any other elements
171
	 * that are still being managed by the transactional editing domain have
172
	 * dependencies on this <tt>eobject</tt>, then existing undo/redo
173
	 * information for these may be corrupted and future undo recording may not
174
	 * be complete.
175
	 * </p><p>
176
	 * It is probably more useful to
177
	 * {@linkplain #disconnectFromEditingDomain(Resource) disconnect} an entire
178
	 * {@link Resource} from the editing domain instead of just an object,
179
	 * unless that object is being moved from one editing domain to another.
180
	 * </p>
181
	 * 
182
	 * @param eobject a model element to disconnect from its current editing
183
	 *     domain, if any
184
	 * 
185
	 * @throws IllegalStateException if the specified <tt>eobject</tt> is
186
	 *     still in the {@link ResourceSet} managed by its current editing
187
	 *     domain
188
	 * 
189
	 * @since 1.1
190
	 * 
191
	 * @see #disconnectFromEditingDomain(Resource)i
192
	 */
193
	public static void disconnectFromEditingDomain(EObject eobject) {
194
		disconnectFromEditingDomain0(eobject);
195
	}
196
	
197
	/**
198
	 * Implements the disconnection of any notifier from the transactional
199
	 * editing domain.
200
	 * 
201
	 * @param notifier the notifier to disconnect
202
	 */
203
	private static void disconnectFromEditingDomain0(Notifier notifier) {
204
		TransactionChangeRecorder recorder = getExistingChangeRecorder(notifier);
205
		
206
		if (recorder != null) {
207
			// this resource is managed by a transactional editing domain
208
			InternalTransactionalEditingDomain domain =
209
				(InternalTransactionalEditingDomain) getEditingDomain(notifier);
210
			
211
			if ((domain != null) && (domain.getChangeRecorder() == recorder)) {
212
				throw new IllegalArgumentException("resource is still in the domain's resource set"); //$NON-NLS-1$
213
			}
214
			
215
			Iterator iter = EcoreUtil.getAllProperContents(Collections.singleton(
216
					notifier), false);
217
			while (iter.hasNext()) {
218
				((Notifier) iter.next()).eAdapters().remove(recorder);
219
			}
220
		}
221
	}
222
	
223
	/**
224
	 * Obtains the transaction change recorder currently attached to a notifier.
225
	 * 
226
	 * @param notifier a notifier
227
	 * @return the currently attached change recorder, or <code>null</code> if none
228
	 */
229
	private static TransactionChangeRecorder getExistingChangeRecorder(Notifier notifier) {
230
		TransactionChangeRecorder result = null;
231
		
232
		Object[] adapters = notifier.eAdapters().toArray();
233
		for (int i = 0; (i < adapters.length) && (result == null); i++) {
234
			if (adapters[i] instanceof TransactionChangeRecorder) {
235
				result = (TransactionChangeRecorder) adapters[i];
236
			}
237
		}
238
		
239
		return result;
240
	}
128
}
241
}
(-)src/org/eclipse/emf/transaction/impl/TransactionChangeRecorder.java (-4 / +38 lines)
Lines 28-33 Link Here
28
import org.eclipse.emf.ecore.change.util.ChangeRecorder;
28
import org.eclipse.emf.ecore.change.util.ChangeRecorder;
29
import org.eclipse.emf.ecore.resource.Resource;
29
import org.eclipse.emf.ecore.resource.Resource;
30
import org.eclipse.emf.ecore.resource.ResourceSet;
30
import org.eclipse.emf.ecore.resource.ResourceSet;
31
import org.eclipse.emf.ecore.util.EcoreUtil;
31
import org.eclipse.emf.ecore.util.InternalEList;
32
import org.eclipse.emf.ecore.util.InternalEList;
32
import org.eclipse.emf.transaction.NotificationFilter;
33
import org.eclipse.emf.transaction.NotificationFilter;
33
import org.eclipse.emf.transaction.internal.EMFTransactionPlugin;
34
import org.eclipse.emf.transaction.internal.EMFTransactionPlugin;
Lines 53-58 Link Here
53
	
54
	
54
	private boolean paused;
55
	private boolean paused;
55
	
56
	
57
	private boolean disposed;
58
	
56
	/**
59
	/**
57
	 * Initializes me with the editing domain that I assist and the resource
60
	 * Initializes me with the editing domain that I assist and the resource
58
	 * set in which I will record changes.  Note that I do not begin recording
61
	 * set in which I will record changes.  Note that I do not begin recording
Lines 68-75 Link Here
68
		// super already started recording
71
		// super already started recording
69
		endRecording();
72
		endRecording();
70
		
73
		
71
		// TODO: Restore when API available
74
		setResolveProxies(false);
72
		//setResolveProxies(false);
73
		
75
		
74
		// tell the resource set manager about any resources that already exist
76
		// tell the resource set manager about any resources that already exist
75
		ResourceSetManager.getInstance().observe(rset);
77
		ResourceSetManager.getInstance().observe(rset);
Lines 119-126 Link Here
119
	 * </ul>
121
	 * </ul>
120
	 */
122
	 */
121
	public void setTarget(Notifier target) {
123
	public void setTarget(Notifier target) {
122
		// TODO: Restore when API available
124
		Iterator contents = target instanceof EObject ? isResolveProxies() ? ((EObject) target).eContents().iterator()
123
		Iterator contents = target instanceof EObject ? /*resolveProxies*/false ? ((EObject) target).eContents().iterator()
124
				: ((InternalEList) ((EObject) target).eContents()).basicIterator()
125
				: ((InternalEList) ((EObject) target).eContents()).basicIterator()
125
				: target instanceof ResourceSet ? ((ResourceSet) target)
126
				: target instanceof ResourceSet ? ((ResourceSet) target)
126
						.getResources().iterator()
127
						.getResources().iterator()
Lines 143-148 Link Here
143
	 * transaction (if any).
144
	 * transaction (if any).
144
	 */
145
	 */
145
	public void notifyChanged(Notification notification) {
146
	public void notifyChanged(Notification notification) {
147
		if (disposed) {
148
			// I am disposed, so I should no longer be responding to or even
149
			//    receiving events.  Moreover, I should not propagate myself
150
			//    to any elements that are later added to me!
151
			Object notifier = notification.getNotifier();
152
			if (notifier instanceof Notifier) {
153
				removeAdapter((Notifier) notifier);
154
			}
155
			
156
			return;
157
		}
158
		
146
		boolean record = true;
159
		boolean record = true;
147
		
160
		
148
		switch (notification.getEventType()) {
161
		switch (notification.getEventType()) {
Lines 341-344 Link Here
341
		
354
		
342
		paused = false;
355
		paused = false;
343
	}
356
	}
357
	
358
	/**
359
	 * Extends the inherited implementation to remove myself from all adapters
360
	 * that I can find in my editing domain.
361
	 * 
362
	 * @since 1.1
363
	 */
364
	public void dispose() {
365
		super.dispose();
366
		
367
		this.disposed = true;
368
		
369
		ResourceSet rset = domain.getResourceSet();
370
		if (rset != null) {
371
			removeAdapter(rset);
372
			
373
			for (Iterator iter = EcoreUtil.getAllProperContents(rset, false); iter.hasNext();) {
374
				removeAdapter((Notifier) iter.next());
375
			}
376
		}
377
	}
344
}
378
}

Return to bug 161169