Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2016-02-02 19:26:56 +0000
committerChristian W. Damus2016-02-02 21:22:46 +0000
commit5808a056ea10850dc850f67f8607e20f027e51a3 (patch)
tree2deef3def13597170dd7b07ceac3f8b423edd665
parent47808f46fa6baeccc0074b9eabc7e29d6c461da6 (diff)
downloadorg.eclipse.papyrus-5808a056ea10850dc850f67f8607e20f027e51a3.tar.gz
org.eclipse.papyrus-5808a056ea10850dc850f67f8607e20f027e51a3.tar.xz
org.eclipse.papyrus-5808a056ea10850dc850f67f8607e20f027e51a3.zip
Bug 487027: [Welcome Page] AssertionFailedException
https://bugs.eclipse.org/bugs/show_bug.cgi?id=487027 Ensure proper disposal of observables for diagram/table views and languages when the Welcome Page is closed, by leveraging the reference-counted observables framework. Change-Id: I671650e54ca01c4072282109b47efdac52ab28e3
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguageObservable.java6
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguagesObservableProperty.java (renamed from plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguagesObservableList.java)41
-rw-r--r--plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/WelcomeModelElement.java4
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservable.java6
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservableProperty.java (renamed from plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservableList.java)42
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/WelcomeModelElement.java4
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java13
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java5
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java113
-rw-r--r--tests/junit/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.tests/src/org/eclipse/papyrus/infra/editor/welcome/tests/WelcomeModelElementTest.java22
-rw-r--r--tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome.tests/src/org/eclipse/papyrus/infra/gmfdiag/welcome/tests/WelcomeModelElementTest.java59
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.classpath2
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.settings/org.eclipse.jdt.core.prefs6
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF2
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java13
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java38
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIteratorContainmentTest.java125
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIteratorTest.java188
18 files changed, 605 insertions, 84 deletions
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguageObservable.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguageObservable.java
index e1738730640..a16fc2d790d 100644
--- a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguageObservable.java
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguageObservable.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -13,19 +13,19 @@
package org.eclipse.papyrus.infra.editor.welcome.internal.modelelements;
-import org.eclipse.core.databinding.observable.AbstractObservable;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.papyrus.infra.core.language.ILanguage;
import org.eclipse.papyrus.infra.core.language.Version;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable;
import org.eclipse.papyrus.infra.tools.databinding.TouchableValue;
/**
* An observable encapsulation of an {@link ILanguage}.
*/
-public class LanguageObservable extends AbstractObservable {
+public class LanguageObservable extends ReferenceCountedObservable.Abstract {
private final ILanguage language;
private final TouchableValue<String> nameValue;
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguagesObservableList.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguagesObservableProperty.java
index 0c897ead6f0..c0d5bebf99d 100644
--- a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguagesObservableList.java
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/LanguagesObservableProperty.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -13,10 +13,11 @@
package org.eclipse.papyrus.infra.editor.welcome.internal.modelelements;
-import java.util.ArrayList;
import java.util.Collection;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
+import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.papyrus.infra.core.language.ILanguage;
import org.eclipse.papyrus.infra.core.language.ILanguageChangeListener;
import org.eclipse.papyrus.infra.core.language.ILanguageService;
@@ -28,54 +29,58 @@ import org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForResourceSet;
import org.eclipse.papyrus.infra.tools.databinding.WritableListWithIterator;
/**
- * An observable list of the {@code LanguageObservable}s encapsulating the
+ * A list property of the {@code LanguageObservable}s encapsulating the
* {@link ILanguage}s instantiated in the model.
*/
-public class LanguagesObservableList extends WritableListWithIterator<LanguageObservable> {
+public class LanguagesObservableProperty implements Supplier<IObservableList<LanguageObservable>> {
private ILanguageService languageService;
private ILanguageChangeListener languagesListener;
+ private IObservableList<LanguageObservable> list;
- public LanguagesObservableList(WelcomeModelElement owner) {
- super(new ArrayList<>(), LanguageObservable.class);
+ public LanguagesObservableProperty(WelcomeModelElement owner) {
+ super();
+
+ this.list = new WritableListWithIterator.Containment<>(LanguageObservable.class);
try {
this.languageService = ServiceUtilsForResourceSet.getInstance().getService(ILanguageService.class, owner.getDomain().getResourceSet());
+
hookLanguagesListener();
+
+ list.addDisposeListener(event -> {
+ languageService.removeLanguageChangeListener(languagesListener);
+ languagesListener = null;
+ languageService = null;
+ });
} catch (ServiceException e) {
Activator.log.error("Cannot obtain language service. Languages will not be shown.", e); //$NON-NLS-1$
}
}
@Override
- public synchronized void dispose() {
- if (languagesListener != null) {
- languageService.removeLanguageChangeListener(languagesListener);
- languagesListener = null;
- languageService = null;
- }
-
- super.dispose();
+ public IObservableList<LanguageObservable> get() {
+ return list;
}
void hookLanguagesListener() {
languagesListener = event -> {
switch (event.getType()) {
case LanguageChangeEvent.ADDED:
- addAll(event.getLanguages().stream().map(LanguageObservable::new).collect(Collectors.toList()));
+ list.addAll(event.getLanguages().stream().map(LanguageObservable::new).collect(Collectors.toList()));
break;
case LanguageChangeEvent.REMOVED:
- removeAll(getObservables(event.getLanguages()));
+ list.removeAll(getObservables(event.getLanguages()));
break;
}
};
ModelSet modelSet = languageService.getAdapter(ModelSet.class);
- addAll(languageService.getLanguages(modelSet.getURIWithoutExtension(), false).stream().map(LanguageObservable::new).collect(Collectors.toList()));
+ list.addAll(languageService.getLanguages(modelSet.getURIWithoutExtension(), false).stream().map(LanguageObservable::new).collect(Collectors.toList()));
languageService.addLanguageChangeListener(languagesListener);
}
Collection<LanguageObservable> getObservables(Collection<? extends ILanguage> languages) {
- return stream().filter(o -> languages.contains(o.getLanguage())).collect(Collectors.toList());
+ return list.stream().filter(o -> languages.contains(o.getLanguage())).collect(Collectors.toList());
}
}
diff --git a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/WelcomeModelElement.java b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/WelcomeModelElement.java
index 612d2a4fe4c..d532ec98dcb 100644
--- a/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/WelcomeModelElement.java
+++ b/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome/src/org/eclipse/papyrus/infra/editor/welcome/internal/modelelements/WelcomeModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -44,7 +44,7 @@ public class WelcomeModelElement extends EMFModelElement {
result = new RestoreActivePageValue(this);
break;
case LANGUAGES:
- result = new LanguagesObservableList(this);
+ result = new LanguagesObservableProperty(this).get();
break;
default:
result = super.doGetObservable(propertyPath);
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservable.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservable.java
index 14153aa8faa..d53aa6be8db 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservable.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservable.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -13,7 +13,6 @@
package org.eclipse.papyrus.infra.gmfdiag.welcome.internal.modelelements;
-import org.eclipse.core.databinding.observable.AbstractObservable;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.Realm;
@@ -27,6 +26,7 @@ import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.papyrus.infra.gmfdiag.welcome.internal.util.ViewUtil;
import org.eclipse.papyrus.infra.nattable.model.nattable.NattablePackage;
import org.eclipse.papyrus.infra.nattable.model.nattable.Table;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable;
import org.eclipse.papyrus.infra.tools.databinding.TouchableValue;
/**
@@ -37,7 +37,7 @@ import org.eclipse.papyrus.infra.tools.databinding.TouchableValue;
* <li>the view's context element</li>
* </ul>
*/
-public class NotationObservable extends AbstractObservable {
+public class NotationObservable extends ReferenceCountedObservable.Abstract {
private ViewAdapter viewAdapter;
private ContextAdapter contextAdapter;
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservableList.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservableProperty.java
index 74a4b8bb61e..76dab14c590 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservableList.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/NotationObservableProperty.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -13,9 +13,9 @@
package org.eclipse.papyrus.infra.gmfdiag.welcome.internal.modelelements;
-import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.function.Supplier;
+import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
@@ -24,29 +24,33 @@ import org.eclipse.papyrus.infra.tools.databinding.WritableListWithIterator;
import org.eclipse.papyrus.infra.viewpoints.policy.ViewPrototype;
/**
- * An observable list of diagram-observables, tracking all of the diagrams available
+ * A list property of diagram-observables, tracking all of the diagrams available
* in the resource set.
*/
-public class NotationObservableList extends WritableListWithIterator<NotationObservable> {
+public class NotationObservableProperty implements Supplier<IObservableList<NotationObservable>> {
private TransactionalEditingDomain domain;
private ResourceAdapter.Transactional diagramsListener;
- public NotationObservableList(WelcomeModelElement owner) {
- super(new ArrayList<>(), NotationObservable.class);
+ private IObservableList<NotationObservable> list;
+ public NotationObservableProperty(WelcomeModelElement owner) {
+ super();
+
+ this.list = new WritableListWithIterator.Containment<>(NotationObservable.class);
this.domain = (TransactionalEditingDomain) owner.getDomain();
+
hookDiagramsListener();
- }
- @Override
- public synchronized void dispose() {
- if (diagramsListener != null) {
+ list.addDisposeListener(event -> {
diagramsListener.uninstall(domain);
diagramsListener = null;
domain = null;
- }
+ });
+ }
- super.dispose();
+ @Override
+ public IObservableList<NotationObservable> get() {
+ return list;
}
void hookDiagramsListener() {
@@ -57,26 +61,20 @@ public class NotationObservableList extends WritableListWithIterator<NotationObs
resource.getContents().stream()
.filter(ViewPrototype::isViewObject)
.map(NotationObservable::new)
- .forEach(NotationObservableList.this::add);
+ .forEach(list::add);
}
@Override
protected void handleRootAdded(Resource resource, EObject root) {
if (ViewPrototype.isViewObject(root)) {
- add(new NotationObservable(root));
+ list.add(new NotationObservable(root));
}
}
@Override
protected void handleRootRemoved(Resource resource, EObject root) {
if (ViewPrototype.isViewObject(root)) {
- for (Iterator<NotationObservable> iter = iterator(); iter.hasNext();) {
- NotationObservable next = iter.next();
- if (next.getView().getValue() == root) {
- iter.remove();
- next.dispose();
- }
- }
+ list.removeIf(next -> next.getView().getValue() == root);
}
}
};
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/WelcomeModelElement.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/WelcomeModelElement.java
index e7fa9403ac5..3df14a0f52c 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/WelcomeModelElement.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome/src/org/eclipse/papyrus/infra/gmfdiag/welcome/internal/modelelements/WelcomeModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -35,7 +35,7 @@ public class WelcomeModelElement extends EMFModelElement {
switch (propertyPath) {
case VIEWS:
- result = new NotationObservableList(this);
+ result = new NotationObservableProperty(this).get();
break;
default:
result = super.doGetObservable(propertyPath);
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java
index 7d22689c9a9..d96f2694f0c 100644
--- a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 CEA and others.
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,7 @@
*
* Contributors:
* Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 487027
*
*/
package org.eclipse.papyrus.infra.tools.databinding;
@@ -108,14 +109,17 @@ public interface ReferenceCountedObservable extends IObservable {
super(realm);
}
+ @Override
public void retain() {
refCount.retain();
}
+ @Override
public void release() {
refCount.release();
}
+ @Override
public void autorelease() {
refCount.autorelease();
}
@@ -124,7 +128,7 @@ public interface ReferenceCountedObservable extends IObservable {
/**
* A convenient superclass for reference-counted observable values that don't need any other more specific superclass.
*/
- abstract class Value extends AbstractObservableValue implements ReferenceCountedObservable {
+ abstract class Value<T> extends AbstractObservableValue<T> implements ReferenceCountedObservable {
private final Support refCount = new Support(this);
@@ -136,14 +140,17 @@ public interface ReferenceCountedObservable extends IObservable {
super(realm);
}
+ @Override
public void retain() {
refCount.retain();
}
+ @Override
public void release() {
refCount.release();
}
+ @Override
public void autorelease() {
refCount.autorelease();
}
@@ -215,6 +222,7 @@ public interface ReferenceCountedObservable extends IObservable {
private final class ReleaseRunnable implements Runnable {
+ @Override
public void run() {
release();
}
@@ -362,6 +370,7 @@ public interface ReferenceCountedObservable extends IObservable {
}
}
+ @Override
public void handleDispose(DisposeEvent event) {
if (event.getObservable() == get()) {
clear();
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java
index 23ffdf711b0..438f111b664 100644
--- a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -18,14 +18,13 @@ import java.util.Objects;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.Realm;
-import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
/**
* An analogue of the {@link WritableValue} that supports "touches" to send
* change events even though the value is not replaced.
*/
-public class TouchableValue<T> extends AbstractObservableValue<T> {
+public class TouchableValue<T> extends ReferenceCountedObservable.Value<T> {
private final Class<? extends T> type;
private T value;
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java
index d2c3bde7c6b..236d80cfcef 100644
--- a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -15,6 +15,7 @@ package org.eclipse.papyrus.infra.tools.databinding;
import static org.eclipse.core.databinding.observable.Diffs.createListDiff;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -22,14 +23,20 @@ import java.util.ListIterator;
import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.ListDiff;
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
import org.eclipse.core.databinding.observable.list.WritableList;
/**
* A specialization of the core Databindings {@link WritableList} providing
* iterators that support modification.
*/
-public class WritableListWithIterator<E> extends WritableList<E> {
+public class WritableListWithIterator<E> extends WritableList<E> implements ReferenceCountedObservable {
+
+ private final ReferenceCountedObservable.Support refCount = new ReferenceCountedObservable.Support(this);
+
+ private final ListDiffVisitor<E> mutationHook = createMutationHook();
public WritableListWithIterator() {
super();
@@ -55,6 +62,62 @@ public class WritableListWithIterator<E> extends WritableList<E> {
super(realm, collection, elementType);
}
+ //
+ // Mutation hooks
+ //
+
+ void didAdd(E element) {
+ // Pass
+ }
+
+ void didRemove(E element) {
+ // Pass
+ }
+
+ private ListDiffVisitor<E> createMutationHook() {
+ return new ListDiffVisitor<E>() {
+ @Override
+ public void handleAdd(int index, E element) {
+ didAdd(element);
+ }
+
+ @Override
+ public void handleRemove(int index, E element) {
+ didRemove(element);
+ }
+ };
+ }
+
+ @Override
+ protected void fireListChange(ListDiff<E> diff) {
+ diff.accept(mutationHook);
+
+ super.fireListChange(diff);
+ }
+
+ //
+ // Reference counting
+ //
+
+ @Override
+ public void retain() {
+ refCount.retain();
+ }
+
+ @Override
+ public void release() {
+ refCount.release();
+ }
+
+ @Override
+ public void autorelease() {
+ refCount.autorelease();
+ }
+
+ //
+ // Iteration
+ //
+
@Override
public Iterator<E> iterator() {
getterCalled();
@@ -176,4 +239,50 @@ public class WritableListWithIterator<E> extends WritableList<E> {
}
}
+
+ /**
+ * A specialized writable list that owns its elements via strong (and counted) references.
+ * It does not support wrapping an externally-provided list.
+ */
+ public static class Containment<E extends ReferenceCountedObservable> extends WritableListWithIterator<E> {
+
+ public Containment() {
+ super();
+ }
+
+ public Containment(Object elementType) {
+ super(new ArrayList<E>(), elementType);
+ }
+
+ public Containment(Realm realm, Object elementType) {
+ super(realm, new ArrayList<E>(), elementType);
+ }
+
+ public Containment(Realm realm) {
+ super(realm);
+ }
+
+ @Override
+ public synchronized void dispose() {
+ super.dispose();
+
+ // Release my contained elements
+ wrappedList.forEach(ReferenceCountedObservable::release);
+ wrappedList.clear();
+ }
+
+ @Override
+ void didAdd(E element) {
+ if (element != null) {
+ element.retain();
+ }
+ }
+
+ @Override
+ void didRemove(E element) {
+ if (element != null) {
+ element.release();
+ }
+ }
+ }
}
diff --git a/tests/junit/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.tests/src/org/eclipse/papyrus/infra/editor/welcome/tests/WelcomeModelElementTest.java b/tests/junit/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.tests/src/org/eclipse/papyrus/infra/editor/welcome/tests/WelcomeModelElementTest.java
index 8542083311e..af6075a1f9c 100644
--- a/tests/junit/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.tests/src/org/eclipse/papyrus/infra/editor/welcome/tests/WelcomeModelElementTest.java
+++ b/tests/junit/plugins/infra/editor/org.eclipse.papyrus.infra.editor.welcome.tests/src/org/eclipse/papyrus/infra/editor/welcome/tests/WelcomeModelElementTest.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -117,6 +117,26 @@ public class WelcomeModelElementTest extends AbstractWelcomePageTest {
assertThat(lang.getVersion().getValue().compareTo(new Version("2.5")), not(lessThan(0)));
}
+ /**
+ * Verifies correct and complete disposal of observables when they are no longer needed.
+ */
+ @Test
+ public void dispose_bug487027() {
+ @SuppressWarnings("unchecked")
+ IObservableList<LanguageObservable> languages = (IObservableList<LanguageObservable>) fixture.getObservable("languages");
+
+ assumeThat(languages, hasItem(anything()));
+ LanguageObservable lang = languages.get(0);
+
+ // Dispose the model-element
+ fixture.dispose();
+
+ // Let the auto-release pool clean up
+ editor.flushDisplayEvents();
+
+ assertThat("Language observable not disposed", lang.isDisposed(), is(true));
+ }
+
@Test
public void isEditable() {
assertThat(fixture.isEditable("privateLayout"), is(true));
diff --git a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome.tests/src/org/eclipse/papyrus/infra/gmfdiag/welcome/tests/WelcomeModelElementTest.java b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome.tests/src/org/eclipse/papyrus/infra/gmfdiag/welcome/tests/WelcomeModelElementTest.java
index c55c9ff27ed..9bfa0aa6be2 100644
--- a/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome.tests/src/org/eclipse/papyrus/infra/gmfdiag/welcome/tests/WelcomeModelElementTest.java
+++ b/tests/junit/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.welcome.tests/src/org/eclipse/papyrus/infra/gmfdiag/welcome/tests/WelcomeModelElementTest.java
@@ -20,6 +20,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeThat;
import java.util.Collection;
import java.util.Collections;
@@ -124,18 +125,7 @@ public class WelcomeModelElementTest extends AbstractWelcomePageTest {
}
});
- editor.execute(new RecordingCommand(editor.getEditingDomain(), "Create Diagram") {
-
- @Override
- protected void doExecute() {
- // Be resilient against misconfigured diagrams: look for the class diagram, specifically
- ViewPrototype prototype = PolicyChecker.getCurrent().getPrototypesFor(editor.getModel()).stream()
- .filter(proto -> proto.getConfiguration() instanceof PapyrusDiagram)
- .filter(proto -> EcoreUtil.getURI(proto.getConfiguration()).toString().contains("org.eclipse.papyrus.uml.diagram.clazz"))
- .findAny().get();
- prototype.instantiateOn(editor.getModel(), "CreatedInTest");
- }
- });
+ createSomeDiagram();
assertThat("List did not notify", created[0], notNullValue());
assertThat(views.size(), is(7));
@@ -148,6 +138,36 @@ public class WelcomeModelElementTest extends AbstractWelcomePageTest {
assertThat(fixture.isEditable("garbage"), is(false));
}
+ /**
+ * Verifies correct and complete disposal of observables when they are no longer needed.
+ */
+ @Test
+ public void dispose_bug487027() {
+ IObservableList<NotationObservable> views = getNotationViews();
+
+ NotationObservable[] obs = { null };
+
+ views.addListChangeListener(event -> {
+ for (ListDiffEntry<? extends NotationObservable> next : event.diff.getDifferences()) {
+ if (next.isAddition() && "CreatedInTest".equals(getName(next.getElement().getView().getValue()))) {
+ obs[0] = next.getElement();
+ }
+ }
+ });
+
+ createSomeDiagram();
+
+ assumeThat("Didn't get the created notation observable", obs[0], notNullValue());
+
+ // Dispose the model-element
+ fixture.dispose();
+
+ // Let the auto-release pool clean up
+ editor.flushDisplayEvents();
+
+ assertThat("Notation observable not disposed", obs[0].isDisposed(), is(true));
+ }
+
//
// Test framework
//
@@ -175,4 +195,19 @@ public class WelcomeModelElementTest extends AbstractWelcomePageTest {
EStructuralFeature nameAttribute = object.eClass().getEStructuralFeature("name");
return (String) object.eGet(nameAttribute);
}
+
+ void createSomeDiagram() {
+ editor.execute(new RecordingCommand(editor.getEditingDomain(), "Create Diagram") {
+
+ @Override
+ protected void doExecute() {
+ // Be resilient against misconfigured diagrams: look for the class diagram, specifically
+ ViewPrototype prototype = PolicyChecker.getCurrent().getPrototypesFor(editor.getModel()).stream()
+ .filter(proto -> proto.getConfiguration() instanceof PapyrusDiagram)
+ .filter(proto -> EcoreUtil.getURI(proto.getConfiguration()).toString().contains("org.eclipse.papyrus.uml.diagram.clazz"))
+ .findAny().get();
+ prototype.instantiateOn(editor.getModel(), "CreatedInTest");
+ }
+ });
+ }
}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.classpath b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.classpath
index ad32c83a788..eca7bdba8f0 100644
--- a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.classpath
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.settings/org.eclipse.jdt.core.prefs b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.settings/org.eclipse.jdt.core.prefs
index 94d61f00da6..b3aa6d60f94 100644
--- a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.settings/org.eclipse.jdt.core.prefs
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/.settings/org.eclipse.jdt.core.prefs
@@ -1,10 +1,10 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF
index 55e768039b8..b12fb347e45 100644
--- a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF
@@ -16,4 +16,4 @@ Bundle-Version: 1.2.0.qualifier
Bundle-Name: Papyrus Infrastructure Tools Tests
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.eclipse.papyrus.infra.tools.tests;singleton:=true
-Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java
index bcb4fd3f0a5..cbbb6ba3bb4 100644
--- a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 CEA and others.
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,12 +8,13 @@
*
* Contributors:
* Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 487027
*
*/
package org.eclipse.papyrus.infra.tools.databinding;
-import org.junit.runner.RunWith;
import org.eclipse.papyrus.junit.framework.classification.ClassificationSuite;
+import org.junit.runner.RunWith;
import org.junit.runners.Suite.SuiteClasses;
@@ -21,7 +22,13 @@ import org.junit.runners.Suite.SuiteClasses;
* The test suite for data-bindings API.
*/
@RunWith(ClassificationSuite.class)
-@SuiteClasses({ DelegatingObservableValueTest.class, DelegatingObservableSetTest.class, DelegatingObservableListTest.class })
+@SuiteClasses({
+ DelegatingObservableValueTest.class,
+ DelegatingObservableSetTest.class,
+ DelegatingObservableListTest.class,
+ WritableListWithIteratorTest.class,
+ WritableListWithIteratorContainmentTest.class,
+})
public class AllDataBindingTests {
private AllDataBindingTests() {
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java
index c5bce9bd2d2..ce8c9ea3ac4 100644
--- a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 CEA and others.
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,7 @@
*
* Contributors:
* Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 487027
*
*/
package org.eclipse.papyrus.infra.tools.databinding;
@@ -16,16 +17,20 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
+import java.util.stream.Stream;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.ObservableTracker;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.papyrus.junit.framework.classification.ClassificationRunner;
+import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
@@ -38,8 +43,25 @@ import org.junit.runners.model.Statement;
*/
public class RealmRunner extends ClassificationRunner {
- public RealmRunner(Class<?> klass) throws InitializationError {
+ private final Field realmField;
+
+ public RealmRunner(@SuppressWarnings("rawtypes") Class klass) throws InitializationError {
super(klass);
+
+ realmField = Stream.iterate(klass, Class::getSuperclass)
+ .flatMap(c -> Stream.of(c.getDeclaredFields()))
+ .filter(f -> Modifier.isStatic(f.getModifiers()))
+ .filter(f -> f.getType() == Realm.class)
+ .findAny().get();
+ realmField.setAccessible(true);
+ }
+
+ private TestRealm getRealm() throws Exception {
+ return ((TestRealm) realmField.get(null));
+ }
+
+ private void setRealm(TestRealm realm) throws Exception {
+ realmField.set(null, realm);
}
@Override
@@ -49,12 +71,12 @@ public class RealmRunner extends ClassificationRunner {
@Override
public void evaluate() throws Throwable {
- DelegatingObservableTest.realm = new TestRealm();
+ setRealm(new TestRealm());
try {
base.evaluate();
} finally {
- ((TestRealm) DelegatingObservableTest.realm).dispose();
- DelegatingObservableTest.realm = null;
+ getRealm().dispose();
+ setRealm(null);
}
}
};
@@ -77,7 +99,11 @@ public class RealmRunner extends ClassificationRunner {
}
};
- DelegatingObservableTest.realm.exec(run);
+ try {
+ getRealm().exec(run);
+ } catch (Exception e) {
+ notifier.fireTestFailure(new Failure(describeChild(method), e));
+ }
run.await();
}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIteratorContainmentTest.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIteratorContainmentTest.java
new file mode 100644
index 00000000000..ca44b521fde
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIteratorContainmentTest.java
@@ -0,0 +1,125 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.papyrus.junit.utils.rules.HouseKeeper;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for the {@link WritableListWithIterator.Containment} class.
+ */
+@RunWith(RealmRunner.class)
+public class WritableListWithIteratorContainmentTest {
+
+ protected static Realm realm;
+
+ @Rule
+ public HouseKeeper houseKeeper = new HouseKeeper();
+
+ private WritableListWithIterator.Containment<MutableString> fixture;
+
+
+ @Test
+ public void testRetainRelease() {
+ MutableString a = MutableString.of("a");
+ MutableString b = MutableString.of("b");
+ MutableString c = MutableString.of("c");
+
+ c.retain(); // Claim this one
+
+ fixture.addAll(Arrays.asList(a, b, c));
+
+ assertThat(b.isDisposed(), is(false));
+
+ fixture.remove(b);
+
+ assertThat(b.isDisposed(), is(true));
+
+ c.release();
+
+ assertThat(c.isDisposed(), is(false)); // The list still has it
+
+ fixture.remove(c);
+
+ assertThat(c.isDisposed(), is(true));
+ }
+
+ @Test
+ public void testDispose() {
+ MutableString a = MutableString.of("a");
+ MutableString b = MutableString.of("b");
+ MutableString c = MutableString.of("c");
+
+ fixture.addAll(Arrays.asList(a, b, c));
+
+ assertThat(a.isDisposed(), is(false));
+ assertThat(b.isDisposed(), is(false));
+ assertThat(c.isDisposed(), is(false));
+
+ fixture.dispose();
+
+ assertThat(a.isDisposed(), is(true));
+ assertThat(b.isDisposed(), is(true));
+ assertThat(c.isDisposed(), is(true));
+ }
+
+ //
+ // Test framework
+ //
+
+ @Before
+ public void createFixture() {
+ fixture = houseKeeper.cleanUpLater(new WritableListWithIterator.Containment<>(realm));
+ }
+
+ static class MutableString extends ReferenceCountedObservable.Value<String> {
+
+ private String value;
+
+ private MutableString() {
+ super(realm);
+ }
+
+ public static MutableString of(String value) {
+ MutableString result = new MutableString();
+ result.setValue(value);
+ return result;
+ }
+
+ @Override
+ public Object getValueType() {
+ return String.class;
+ }
+
+ @Override
+ protected void doSetValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ protected String doGetValue() {
+ return value;
+ }
+
+ }
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIteratorTest.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIteratorTest.java
new file mode 100644
index 00000000000..fae4e0037d4
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIteratorTest.java
@@ -0,0 +1,188 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IListChangeListener;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListChangeEvent;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable.AutoReleasePool;
+import org.eclipse.papyrus.junit.utils.rules.HouseKeeper;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cases for the {@link WritableListWithIterator} class.
+ */
+@RunWith(RealmRunner.class)
+public class WritableListWithIteratorTest {
+
+ protected static Realm realm;
+
+ @Rule
+ public HouseKeeper houseKeeper = new HouseKeeper();
+
+ private WritableListWithIterator<String> fixture;
+
+ @Test
+ public void testRetain() {
+ fixture.retain();
+ fixture.retain();
+
+ fixture.release();
+
+ assertThat(fixture.isDisposed(), is(false));
+ }
+
+ @Test
+ public void testRelease() {
+ fixture.retain();
+
+ fixture.release();
+
+ assertThat(fixture.isDisposed(), is(true));
+ }
+
+ @Test
+ public void testAutorelease() throws Exception {
+ fixture.retain();
+
+ fixture.autorelease();
+
+ assertThat(fixture.isDisposed(), is(false));
+
+ // Drain the pending autorelease pool
+ AutoReleasePool.get(realm).release();
+
+ assertThat(fixture.isDisposed(), is(true));
+ }
+
+ @Test
+ public void testIterator() {
+ fixture.addAll(Arrays.asList("a", "b", "c"));
+
+ ListChangeAssertion<String> changes = ListChangeAssertion.on(fixture);
+
+ Iterator<String> iter = fixture.iterator();
+ assertThat(iter.next(), is("a"));
+ assertThat(iter.next(), is("b"));
+ iter.remove();
+ changes.assertRemoved("b");
+ assertThat(iter.next(), is("c"));
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void testListIterator() {
+ fixture.addAll(Arrays.asList("a", "b", "c"));
+
+ ListChangeAssertion<String> changes = ListChangeAssertion.on(fixture);
+
+ ListIterator<String> iter = fixture.listIterator();
+ assertThat(iter.next(), is("a"));
+ assertThat(iter.next(), is("b"));
+ iter.remove();
+ changes.assertRemoved("b");
+ iter.add("d");
+ changes.assertAdded("d");
+ assertThat(iter.next(), is("c"));
+ iter.set("e");
+ changes.assertRemoved("c");
+ changes.assertAdded("e");
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void testListIterator_int() {
+ fixture.addAll(Arrays.asList("a", "b", "c"));
+
+ ListChangeAssertion<String> changes = ListChangeAssertion.on(fixture);
+
+ ListIterator<String> iter = fixture.listIterator(3);
+ assertThat(iter.previous(), is("c"));
+ assertThat(iter.previous(), is("b"));
+ iter.remove();
+ changes.assertRemoved("b");
+ iter.add("d");
+ changes.assertAdded("d");
+ assertThat(iter.previous(), is("d"));
+ assertThat(iter.previous(), is("a"));
+ iter.set("e");
+ changes.assertRemoved("a");
+ changes.assertAdded("e");
+ assertThat(iter.hasPrevious(), is(false));
+ }
+
+ //
+ // Test framework
+ //
+
+ @Before
+ public void createFixture() {
+ fixture = houseKeeper.cleanUpLater(new WritableListWithIterator<>(realm));
+ }
+
+ static class ListChangeAssertion<E> implements IListChangeListener<E> {
+ private List<E> added = new ArrayList<>();
+ private List<E> removed = new ArrayList<>();
+
+ public static <E> ListChangeAssertion<E> on(IObservableList<E> list) {
+ ListChangeAssertion<E> result = new ListChangeAssertion<>();
+ list.addListChangeListener(result);
+ return result;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public void handleListChange(ListChangeEvent<? extends E> event) {
+ event.diff.accept((ListDiffVisitor) new ListDiffVisitor<E>() {
+ @Override
+ public void handleAdd(int index, E element) {
+ added.add(element);
+ }
+
+ @Override
+ public void handleRemove(int index, E element) {
+ removed.add(element);
+ }
+ });
+ }
+
+ public void assertAdded(E element) {
+ assertThat(added, hasItem(element));
+ }
+
+ public void assertRemoved(E element) {
+ assertThat(removed, hasItem(element));
+ }
+
+ public void reset() {
+ added.clear();
+ removed.clear();
+ }
+ }
+}

Back to the top