### Eclipse Workspace Patch 1.0 #P org.eclipse.gmf.runtime.emf.core Index: src/org/eclipse/gmf/runtime/emf/core/internal/notifications/MSLObjectListener.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/plugins/org.eclipse.gmf.runtime.emf.core/src/org/eclipse/gmf/runtime/emf/core/internal/notifications/MSLObjectListener.java,v retrieving revision 1.2 diff -u -r1.2 MSLObjectListener.java --- src/org/eclipse/gmf/runtime/emf/core/internal/notifications/MSLObjectListener.java 12 Sep 2005 21:25:11 -0000 1.2 +++ src/org/eclipse/gmf/runtime/emf/core/internal/notifications/MSLObjectListener.java 15 Dec 2005 15:16:48 -0000 @@ -19,9 +19,7 @@ import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; - import org.eclipse.gmf.runtime.emf.core.EventTypes; import org.eclipse.gmf.runtime.emf.core.internal.domain.MSLEditingDomain; import org.eclipse.gmf.runtime.emf.core.internal.resources.MResource; @@ -59,27 +57,6 @@ if (!domain.getResouceListener().resourceFinishedLoading(resource)) return; - // set resource dirty flag. - switch (eventType) { - case Notification.SET: - case Notification.UNSET: - case Notification.MOVE: { - if (!notification.isTouch() && !resource.isModified()){ - if(!isTransient(notifier, feature)) - resource.setModified(true); - } - break; - } - - case Notification.ADD: - case Notification.REMOVE: - case Notification.ADD_MANY: - case Notification.REMOVE_MANY: { - if (!resource.isModified() && !isTransient(notifier, feature)) - resource.setModified(true); - break; - } - } } Object newValue = notification.getNewValue(); @@ -121,41 +98,6 @@ } /** - * check if the feature or one of its containers is transient - * @param notifier - * @param feature - * @param transientChange - * @return - */ - private boolean isTransient(EObject notifier, Object feature) { - if (feature instanceof EStructuralFeature) { - if (((EStructuralFeature) feature).isTransient()) - return true; - else - // calling isTransient could be a lengthy operation - return isTransient(notifier); - } - return false; - } - - /** - * Is object transient? - */ - private boolean isTransient(EObject eObject) { - EStructuralFeature containmentFeature = eObject.eContainmentFeature(); - while (containmentFeature != null) { - if (containmentFeature.isTransient()) - return true; - eObject = eObject.eContainer(); - if (eObject != null) - containmentFeature = eObject.eContainmentFeature(); - else - break; - } - return false; - } - - /** * Process references and update reference maps. */ private void processReferences(EObject notifier, int eventType, Index: src/org/eclipse/gmf/runtime/emf/core/internal/resources/LogicalResourceUnit.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/plugins/org.eclipse.gmf.runtime.emf.core/src/org/eclipse/gmf/runtime/emf/core/internal/resources/LogicalResourceUnit.java,v retrieving revision 1.2 diff -u -r1.2 LogicalResourceUnit.java --- src/org/eclipse/gmf/runtime/emf/core/internal/resources/LogicalResourceUnit.java 12 Sep 2005 21:25:12 -0000 1.2 +++ src/org/eclipse/gmf/runtime/emf/core/internal/resources/LogicalResourceUnit.java 15 Dec 2005 15:16:49 -0000 @@ -525,7 +525,7 @@ public void setModified(boolean isModified) { super.setModified(isModified); - if (isModified) { + if (isModified && getLogicalResource().isTrackingModification()) { // propagate modified state to the root getLogicalResource().setModified(isModified); } Index: src/org/eclipse/gmf/runtime/emf/core/internal/resources/MSLResource.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/plugins/org.eclipse.gmf.runtime.emf.core/src/org/eclipse/gmf/runtime/emf/core/internal/resources/MSLResource.java,v retrieving revision 1.2 diff -u -r1.2 MSLResource.java --- src/org/eclipse/gmf/runtime/emf/core/internal/resources/MSLResource.java 12 Sep 2005 21:25:12 -0000 1.2 +++ src/org/eclipse/gmf/runtime/emf/core/internal/resources/MSLResource.java 15 Dec 2005 15:16:49 -0000 @@ -11,8 +11,11 @@ package org.eclipse.gmf.runtime.emf.core.internal.resources; +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.gmf.runtime.emf.core.edit.MEditingDomain; import org.eclipse.gmf.runtime.emf.core.internal.util.MSLConstants; @@ -26,13 +29,15 @@ extends LogicalResource { private boolean useIDAttributes = false; - + /** * Constructor. */ public MSLResource(URI uri) { super(MEditingDomain.INSTANCE.convertURI(uri)); + + setTrackingModification(true); } protected LogicalResourceUnit createUnit(URI unitUri) { @@ -98,4 +103,64 @@ super.setURI(uri); } + + /** + * The inherited implementation creates an adapter that always sets + * the modified state. We prefer to check, first, whether the resource + * is already modified so that we don't generate redundant notifications. + * Moreover, we additionally set modified state only for changes that are + * in non-transient features of objects contained (recursively) by + * non-transient references. + */ + protected Adapter createModificationTrackingAdapter() { + return new ModificationTrackingAdapter() { + public void notifyChanged(Notification notification) { + if (!isModified() && !isTransient( + notification.getNotifier(), notification.getFeature())) { + + super.notifyChanged(notification); + } + } + + /** + * Check if the feature or one of the notifier's containers is + * transient. + * + * @param notifier a notifier + * @param feature the feature that changed + * + * @return true if the feature is transient or if the + * notifier or any of its ancestors is contained by a transient + * reference; false, otherwise + */ + private boolean isTransient(Object notifier, Object feature) { + if (feature instanceof EStructuralFeature) { + if (((EStructuralFeature) feature).isTransient()) + return true; + else + // calling isTransient could be a lengthy operation. + // It is safe to cast because the adapter is only + // attached to EObjects, not to the resource + return isTransient((EObject) notifier); + } + return false; + } + + /** + * Is object transient? + */ + private boolean isTransient(EObject eObject) { + EStructuralFeature containmentFeature = eObject.eContainmentFeature(); + while (containmentFeature != null) { + if (containmentFeature.isTransient()) + return true; + eObject = eObject.eContainer(); + if (eObject != null) + containmentFeature = eObject.eContainmentFeature(); + else + break; + } + return false; + }}; + } } \ No newline at end of file #P org.eclipse.gmf.tests.runtime.emf.core Index: src/org/eclipse/gmf/tests/runtime/emf/core/BaseCoreTests.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/tests/org.eclipse.gmf.tests.runtime.emf.core/src/org/eclipse/gmf/tests/runtime/emf/core/BaseCoreTests.java,v retrieving revision 1.5 diff -u -r1.5 BaseCoreTests.java --- src/org/eclipse/gmf/tests/runtime/emf/core/BaseCoreTests.java 25 Nov 2005 21:58:28 -0000 1.5 +++ src/org/eclipse/gmf/tests/runtime/emf/core/BaseCoreTests.java 15 Dec 2005 15:16:50 -0000 @@ -27,9 +27,7 @@ import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; -import org.osgi.framework.Bundle; - -import org.eclipse.emf.examples.extlibrary.AudoVisualItem; +import org.eclipse.emf.examples.extlibrary.AudioVisualItem; import org.eclipse.emf.examples.extlibrary.Book; import org.eclipse.emf.examples.extlibrary.Library; import org.eclipse.emf.examples.extlibrary.Periodical; @@ -41,6 +39,7 @@ import org.eclipse.gmf.runtime.emf.core.edit.MRunnable; import org.eclipse.gmf.runtime.emf.core.edit.MUndoInterval; import org.eclipse.gmf.runtime.emf.core.exceptions.MSLActionAbandonedException; +import org.osgi.framework.Bundle; /** @@ -409,7 +408,7 @@ super(); } - public Object caseAudoVisualItem(AudoVisualItem object) { + public Object caseAudoVisualItem(AudioVisualItem object) { return object.getTitle(); } Index: src/org/eclipse/gmf/tests/runtime/emf/core/RegressionTestCase.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/tests/org.eclipse.gmf.tests.runtime.emf.core/src/org/eclipse/gmf/tests/runtime/emf/core/RegressionTestCase.java,v retrieving revision 1.4 diff -u -r1.4 RegressionTestCase.java --- src/org/eclipse/gmf/tests/runtime/emf/core/RegressionTestCase.java 20 Oct 2005 21:34:21 -0000 1.4 +++ src/org/eclipse/gmf/tests/runtime/emf/core/RegressionTestCase.java 15 Dec 2005 15:16:50 -0000 @@ -15,6 +15,8 @@ import junit.framework.Test; import junit.framework.TestSuite; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.resource.Resource; @@ -303,4 +305,60 @@ // check that MSLUtil made the correct change assertEquals(expected, list); } + + /** + * Tests consistency of the isTrackingModification() method + * with the actual tracking of modification provided by the + * MSLObjectListener. + */ + public void test_modificationTracking_112531() { + class TestAdapter extends AdapterImpl { + boolean modified; + + public void notifyChanged(Notification msg) { + if (msg.getFeatureID(null) == Resource.RESOURCE__IS_MODIFIED) { + modified = msg.getNewBooleanValue(); + } + } + + void reset() { + modified = false; + } + } + + if (writing()) { + // MSL resources initially track modification + assertTrue(testResource.isTrackingModification()); + + TestAdapter adapter = new TestAdapter(); + testResource.eAdapters().add(adapter); + + assertFalse(adapter.modified); + + // make a change + Library lib = EXTLibraryFactory.eINSTANCE.createLibrary(); + lib.setName("NewlyAdded"); //$NON-NLS-1$ + root.getBranches().add(lib); + + assertTrue(adapter.modified); + adapter.reset(); + + // disable modification tracking + testResource.setModified(false); + testResource.setTrackingModification(false); + + // make a change + root.getBranches().remove(lib); + + assertFalse(adapter.modified); + + // re-enable tracking + testResource.setTrackingModification(true); + + // make a change + root.getBranches().add(lib); + + assertTrue(adapter.modified); + } + } } Index: src/org/eclipse/gmf/tests/runtime/emf/core/internal/resources/BaseLogicalResourceTest.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/tests/org.eclipse.gmf.tests.runtime.emf.core/src/org/eclipse/gmf/tests/runtime/emf/core/internal/resources/BaseLogicalResourceTest.java,v retrieving revision 1.3 diff -u -r1.3 BaseLogicalResourceTest.java --- src/org/eclipse/gmf/tests/runtime/emf/core/internal/resources/BaseLogicalResourceTest.java 22 Sep 2005 14:08:49 -0000 1.3 +++ src/org/eclipse/gmf/tests/runtime/emf/core/internal/resources/BaseLogicalResourceTest.java 15 Dec 2005 15:16:50 -0000 @@ -33,9 +33,7 @@ import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; -import org.osgi.framework.Bundle; - -import org.eclipse.emf.examples.extlibrary.AudoVisualItem; +import org.eclipse.emf.examples.extlibrary.AudioVisualItem; import org.eclipse.emf.examples.extlibrary.Book; import org.eclipse.emf.examples.extlibrary.Library; import org.eclipse.emf.examples.extlibrary.Periodical; @@ -51,6 +49,7 @@ import org.eclipse.gmf.runtime.emf.core.internal.resources.AbstractResourceWrapper; import org.eclipse.gmf.runtime.emf.core.internal.resources.LogicalResourceWrapper; import org.eclipse.gmf.runtime.emf.core.resources.ILogicalResource; +import org.osgi.framework.Bundle; /** @@ -581,7 +580,7 @@ super(); } - public Object caseAudoVisualItem(AudoVisualItem object) { + public Object caseAudoVisualItem(AudioVisualItem object) { return object.getTitle(); }