From b6b2c494920214f00144312b6c8756106fda2a8e Mon Sep 17 00:00:00 2001
From: Leon Linhart
Date: Sun, 4 Jun 2023 23:52:48 +0200
Subject: [PATCH] feat(quitte): refactor collection listeners
Closes #10
Closes #11
---
docs/changelog/0.8.0.md | 21 +-
.../quitte/kotlinx/coroutines/Flow.kt | 10 +-
.../collections/AbstractObservableDeque.java | 64 ++--
.../collections/AbstractObservableList.java | 117 ++++---
.../collections/AbstractObservableMap.java | 228 ++++++++++---
.../collections/AbstractObservableSet.java | 32 +-
.../collections/DequeChangeListener.java | 185 +++++++++++
.../collections/ListChangeListener.java | 255 ++++++++++++++
.../quitte/collections/MapChangeListener.java | 109 ++++++
.../collections/ObservableCollection.java | 52 +--
.../quitte/collections/ObservableDeque.java | 223 +++----------
.../quitte/collections/ObservableList.java | 312 +++---------------
.../quitte/collections/ObservableMap.java | 114 +++----
.../quitte/collections/ObservableSet.java | 53 ++-
...geListener.java => SetChangeListener.java} | 58 +++-
.../collections/WeakDequeChangeListener.java | 99 ++++++
.../collections/WeakListChangeListener.java | 99 ++++++
.../collections/WeakMapChangeListener.java | 100 ++++++
...stener.java => WeakSetChangeListener.java} | 31 +-
.../quitte/internal/binding/DequeBinding.java | 50 ++-
.../quitte/internal/binding/ListBinding.java | 60 +++-
.../quitte/internal/binding/MapBinding.java | 22 +-
.../quitte/internal/binding/SetBinding.java | 20 +-
.../UnmodifiableObservableDeque.java | 6 +-
.../UnmodifiableObservableList.java | 6 +-
.../UnmodifiableObservableMap.java | 18 +-
.../UnmodifiableObservableSet.java | 6 +-
.../collections/WrappingObservableDeque.java | 5 +-
.../collections/WrappingObservableMap.java | 10 +-
.../quitte/property/DequeProperty.java | 25 +-
.../quitte/property/ListProperty.java | 35 +-
.../osmerion/quitte/property/MapProperty.java | 38 +--
.../osmerion/quitte/property/SetProperty.java | 3 +-
.../collections/MockDequeChangeListener.java | 28 +-
.../collections/MockListChangeListener.java | 71 +++-
.../collections/MockMapChangeListener.java | 34 +-
.../collections/MockSetChangeListener.java | 47 +--
.../collections/ObservableDequeTest.java | 6 +-
.../collections/ObservableListTest.java | 11 +
.../quitte/collections/ObservableMapTest.java | 36 ++
40 files changed, 1741 insertions(+), 958 deletions(-)
create mode 100644 modules/quitte/src/main/java/com/osmerion/quitte/collections/DequeChangeListener.java
create mode 100644 modules/quitte/src/main/java/com/osmerion/quitte/collections/ListChangeListener.java
create mode 100644 modules/quitte/src/main/java/com/osmerion/quitte/collections/MapChangeListener.java
rename modules/quitte/src/main/java/com/osmerion/quitte/collections/{CollectionChangeListener.java => SetChangeListener.java} (54%)
create mode 100644 modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakDequeChangeListener.java
create mode 100644 modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakListChangeListener.java
create mode 100644 modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakMapChangeListener.java
rename modules/quitte/src/main/java/com/osmerion/quitte/collections/{WeakCollectionChangeListener.java => WeakSetChangeListener.java} (74%)
diff --git a/docs/changelog/0.8.0.md b/docs/changelog/0.8.0.md
index b1837fb..5c2e538 100644
--- a/docs/changelog/0.8.0.md
+++ b/docs/changelog/0.8.0.md
@@ -6,23 +6,14 @@ _Not Released Yet_
- Added an explicit module descriptor (`module-info.java`) to
`quitte-kotlinx-coroutines`.
+- Refactored the collection listener API for improved usability.
+ - Local updates for lists now contain the old elements. [[GH-5](https://github.com/Osmerion/Quitte/issues/10)]
+ - `ObservableMap::entrySet` does now return an `ObservableSet`. [[GH-11](https://github.com/Osmerion/Quitte/issues/11)]
- Various JavaDoc improvements.
-#### Deprecations
-
-- Deprecations in `ObservableMap.Change`:
- - Deprecated `getOldValue()` in favor of the canonical record accessor `oldValue()`.
- - Deprecated `getNewValue()` in favor of the canonical record accessor `newValue()`.
-
#### Breaking Changes
-- Removals in `ObservableMap.Change`:
- - `getAddedElements()` was removed. Use `addedElements()` instead.
- - `getRemovedElements()` was removed. Use `removedElements()` instead.
- - `getUpdatedElements()` was removed. Use `updatedElements()` instead.
- - `copy(BiFunction)` was removed without replacement.
-- Removals in `ObservableSet.Change`:
- - `getAddedElements()` was removed. Use `addedElements()` instead.
- - `getRemovedElements()` was removed. Use `removedElements()` instead.
- - `copy(Function)` was removed without replacement.
+- The collection listener API was refactored for improved usability.
+ Consequentially, all deprecated methods were removed and a few additional API
+ were made.
- Updated [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) dependency to `1.7.1`.
\ No newline at end of file
diff --git a/modules/quitte-kotlinx-coroutines/src/main/java/com/osmerion/quitte/kotlinx/coroutines/Flow.kt b/modules/quitte-kotlinx-coroutines/src/main/java/com/osmerion/quitte/kotlinx/coroutines/Flow.kt
index 31fb4d6..eba5403 100644
--- a/modules/quitte-kotlinx-coroutines/src/main/java/com/osmerion/quitte/kotlinx/coroutines/Flow.kt
+++ b/modules/quitte-kotlinx-coroutines/src/main/java/com/osmerion/quitte/kotlinx/coroutines/Flow.kt
@@ -31,10 +31,12 @@
package com.osmerion.quitte.kotlinx.coroutines
import com.osmerion.quitte.InvalidationListener
-import com.osmerion.quitte.collections.CollectionChangeListener
+import com.osmerion.quitte.collections.ListChangeListener
+import com.osmerion.quitte.collections.MapChangeListener
import com.osmerion.quitte.collections.ObservableList
import com.osmerion.quitte.collections.ObservableMap
import com.osmerion.quitte.collections.ObservableSet
+import com.osmerion.quitte.collections.SetChangeListener
import com.osmerion.quitte.value.ObservableValue
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -80,7 +82,7 @@ public fun ObservableValue.asFlow(): Flow = callbackFlow {
* @since 0.3.0
*/
public fun ObservableList.asFlow(): Flow> = callbackFlow {
- val listener = CollectionChangeListener> {
+ val listener = ListChangeListener { _, _ ->
trySend(this@asFlow.toList())
}
@@ -103,7 +105,7 @@ public fun ObservableList.asFlow(): Flow> = callbackFlow {
* @since 0.3.0
*/
public fun ObservableMap.asFlow(): Flow
*/
@Override
Iterator descendingIterator();
@@ -110,7 +153,7 @@ static ObservableDeque unmodifiableViewOf(ObservableDeque deque) {
/**
* {@inheritDoc}
*
- * This method should be used with caution as it produces {@link Site#OPAQUE opaque changes}.
+ * This method should be used with caution as it produces {@link DequeChangeListener.Site#OPAQUE opaque changes}.
*/
@Override
boolean remove(@Nullable Object o);
@@ -118,7 +161,7 @@ static ObservableDeque unmodifiableViewOf(ObservableDeque deque) {
/**
* {@inheritDoc}
*
- * This method should be used with caution as it produces {@link Site#OPAQUE opaque changes}.
+ * This method should be used with caution as it produces {@link DequeChangeListener.Site#OPAQUE opaque changes}.
*/
@Override
boolean removeFirstOccurrence(Object o);
@@ -126,173 +169,9 @@ static ObservableDeque unmodifiableViewOf(ObservableDeque deque) {
/**
* {@inheritDoc}
*
- * This method should be used with caution as it produces {@link Site#OPAQUE opaque changes}.
+ * This method should be used with caution as it produces {@link DequeChangeListener.Site#OPAQUE opaque changes}.
*/
@Override
boolean removeLastOccurrence(Object o);
- /**
- * A change to a deque consists of one or more {@link LocalChange local updates} that apply to a specific
- * {@link Site site} of the deque.
- *
- * @param the type of the deque's elements
- *
- * @since 0.1.0
- */
- record Change(List> localChanges) {
-
- public Change {
- localChanges = List.copyOf(localChanges);
- }
-
- /**
- * Creates a copy of this change using the given {@code transform} to map the elements.
- *
- * @param the new type for the elements
- * @param transform the transform function to be applied to the elements
- *
- * @return a copy of this change
- *
- * @deprecated This is an unsupported method that may be removed at any time.
- *
- * @since 0.1.0
- */
- @Deprecated
- public Change copy(Function super E, T> transform) {
- return new Change<>(this.localChanges.stream().map(it -> it.copy(transform)).toList());
- }
-
- /**
- * Returns a list of changes that are local to parts of the deque.
- *
- * It is important to process local changes in order, since the order of the elements matters.
- *
- * @return a list of changes that are local to parts of the deque
- *
- * @deprecated Deprecated in favor of canonical record accessor {@link #localChanges()}.
- *
- * @since 0.1.0
- */
- @Deprecated(since = "0.3.0", forRemoval = true)
- public List> getLocalChanges() {
- return this.localChanges;
- }
-
- }
-
- /**
- * A change to a deque. This might either be an {@link Insertion}, or a {@link Removal}.
- *
- * Using {@code instanceof} checks (or similar future pattern matching mechanisms) is recommended when working
- * with {@code LocalChange} objects.
- *
- * @param the type of the deque's elements
- *
- * @since 0.1.0
- */
- abstract class LocalChange {
-
- private final Site site;
- private final List elements;
-
- // TODO This is an ideal candidate for a sealed record hierarchy.
- private LocalChange(Site site, List elements) {
- this.site = site;
- this.elements = elements;
- }
-
- @Deprecated
- abstract LocalChange copy(Function super E, T> transform);
-
- /**
- * A list of elements related to this change. How this list should be interpreted is defined by an implementing
- * class.
- *
- * @return a list of elements related to this change
- *
- * @since 0.1.0
- */
- public final List getElements() {
- return this.elements;
- }
-
- /**
- * The {@link Site site} of the deque to which the change applies.
- *
- * @return site of the deque to which the change applies
- *
- * @since 0.1.0
- */
- public final Site getSite() {
- return this.site;
- }
-
- /**
- * Represents insertion of one or more subsequent {@link #getElements() elements} starting from a given
- * {@link #getSite() site}.
- *
- * @since 0.1.0
- */
- public static final class Insertion extends LocalChange {
-
- Insertion(Site site, List elements) {
- super(site, elements);
- }
-
- @Override
- LocalChange copy(Function super E, T> transform) {
- return new Insertion<>(this.getSite(), this.getElements().stream().map(transform).toList());
- }
-
- }
-
- /**
- * Represents removal of one or more subsequent {@link #getElements() elements} starting from a given
- * {@link #getSite() site}.
- *
- * @since 0.1.0
- */
- public static final class Removal extends LocalChange {
-
- Removal(Site site, List elements) {
- super(site, elements);
- }
-
- @Override
- LocalChange copy(Function super E, T> transform) {
- return new Removal<>(this.getSite(), this.getElements().stream().map(transform).toList());
- }
-
- }
-
- }
-
- /**
- * A modifiable site of a deque.
- *
- * @since 0.1.0
- */
- enum Site {
- /**
- * The "head" (or "front") of the deque.
- *
- * @since 0.1.0
- */
- HEAD,
- /**
- * The "tail" (or "back") of the deque.
- *
- * @since 0.1.0
- */
- TAIL,
- /**
- * An opaque position in the deque.
- *
- * Using operations which produce "opaque change" is discouraged.
- *
- * @since 0.1.0
- */
- OPAQUE
- }
-
}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableList.java b/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableList.java
index 04988e4..e5f9d23 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableList.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableList.java
@@ -32,10 +32,8 @@
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
-import java.util.function.Function;
import com.osmerion.quitte.internal.collections.UnmodifiableObservableList;
import com.osmerion.quitte.internal.collections.UnmodifiableRandomAccessObservableList;
@@ -51,7 +49,7 @@
*
* @author Leon Linhart
*/
-public interface ObservableList extends List, ObservableCollection> {
+public interface ObservableList extends List, ObservableCollection {
/**
* Returns an observable view of the specified list. Query operations on the returned list "read and write through"
@@ -91,6 +89,51 @@ static ObservableList unmodifiableViewOf(ObservableList list) {
return list instanceof RandomAccess ? new UnmodifiableRandomAccessObservableList<>(list) : new UnmodifiableObservableList<>(list);
}
+ /**
+ * Attaches the given {@link ListChangeListener change listener} to this list.
+ *
+ * If the given listener is already attached to this list, this method does nothing and returns {@code false}.
+ *
+ *
+ * While an {@code ListChangeListener} is attached to a list, it will be {@link ListChangeListener#onChanged(ObservableList, ListChangeListener.Change)}
+ * notified} whenever the list is updated.
+ *
+ * This list stores a strong reference to the given listener until the listener is either removed explicitly by
+ * calling {@link #removeChangeListener(ListChangeListener)} or implicitly when this list discovers that the
+ * listener has become {@link ListChangeListener#isInvalid() invalid}. Generally, it is recommended to use an
+ * instance of {@link WeakListChangeListener} when possible to avoid leaking instances.
+ *
+ * @param listener the listener to be attached to this list
+ *
+ * @return {@code true} if the listener was not previously attached to this list and has been successfully
+ * attached, or {@code false} otherwise
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @see #removeChangeListener(ListChangeListener)
+ *
+ * @since 0.8.0
+ */
+ boolean addChangeListener(ListChangeListener super E> listener);
+
+ /**
+ * Detaches the given {@link ListChangeListener change listener} from this list.
+ *
+ * If the given listener is not attached to this list, this method does nothing and returns {@code false}.
+ *
+ * @param listener the listener to be detached from this list
+ *
+ * @return {@code true} if the listener was attached to and has been detached from this list, or {@code false}
+ * otherwise
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @see #addChangeListener(ListChangeListener)
+ *
+ * @since 0.8.0
+ */
+ boolean removeChangeListener(ListChangeListener super E> listener);
+
/**
* See {@link #addAll(Collection)}.
*
@@ -192,267 +235,4 @@ default boolean setAll(E... elements) {
*/
boolean setAll(Collection elements);
- /**
- * A change to a list may either be a {@link Permutation permutation}, or one or more local updates to parts of the
- * list (represented as {@link LocalChange}).
- *
- * Using {@code instanceof} checks (or similar future pattern matching mechanisms) is recommended when working
- * with {@code Change} objects.
- *
- * @param the type of the list's elements
- *
- * @since 0.1.0
- */
- abstract class Change {
-
- private Change() {} // TODO This is an ideal candidate for a sealed record hierarchy.
-
- /**
- * Creates a copy of this change using the given {@code transform} to map the elements.
- *
- * @param the new type for the elements
- * @param transform the transform function to be applied to the elements
- *
- * @return a copy of this change
- *
- * @deprecated This is an unsupported method that may be removed at any time.
- *
- * @since 0.1.0
- */
- @Deprecated
- public abstract Change copy(Function super E, T> transform);
-
- /**
- * A change to a list in which its elements are rearranged.
- *
- * @since 0.1.0
- */
- public static final class Permutation extends Change {
-
- private final List indices;
-
- Permutation(List indices) {
- this.indices = indices;
- }
-
- /**
- * {@inheritDoc}
- *
- * @since 0.1.0
- */
- @SuppressWarnings("unchecked")
- @Deprecated
- @Override
- public Change copy(Function super E, T> transform) {
- return (Permutation) this;
- }
-
- /**
- * A list of integers that represents the mapping of indices used to create the current permutation.
- *
- * @return a list of integers that represents the mapping of indices used to create the current permutation
- *
- * @since 0.1.0
- */
- public List getIndices() {
- return this.indices;
- }
-
- }
-
- /**
- * A change to a list that consists of one or more local changes to the list.
- *
- * @since 0.1.0
- */
- public static final class Update extends Change {
-
- private final List> localChanges;
-
- Update(List> localChanges) {
- this.localChanges = localChanges;
- }
-
- /**
- * {@inheritDoc}
- *
- * @since 0.1.0
- */
- @Deprecated
- @Override
- public Change copy(Function super E, T> transform) {
- return new Update<>(this.localChanges.stream().map(it -> it.copy(transform)).toList());
- }
-
- /**
- * Returns a list of changes that are local to parts of the list.
- *
- * It is important to process local changes in order, since their indices depend on another.
- *
- * @return a list of changes that are local to parts of the list
- *
- * @since 0.1.0
- */
- public List> getLocalChanges() {
- return this.localChanges;
- }
-
- }
-
- }
-
- /**
- * A change to a list. This might either be an {@link Insertion}, a {@link Removal}, or an {@link Update}.
- *
- * Using {@code instanceof} checks (or similar future pattern matching mechanisms) is recommended when working
- * with {@code LocalChange} objects.
- *
- * @param the type of the list's elements
- *
- * @since 0.1.0
- */
- abstract class LocalChange {
-
- private final int index;
- private final List elements;
-
- // TODO This is an ideal candidate for a sealed record hierarchy.
- private LocalChange(int index, List elements) {
- this.index = index;
- this.elements = Collections.unmodifiableList(elements);
- }
-
- @Deprecated
- abstract LocalChange copy(Function super E, T> transform);
-
- /**
- * Returns the index of the first element affected by this change.
- *
- * @return the index of the first element affected by this change
- *
- * @since 0.1.0
- */
- public final int getIndex() {
- return this.index;
- }
-
- /**
- * A list of elements related to this change. How this list should be interpreted is defined by an implementing
- * class.
- *
- * @return a list of elements related to this change
- *
- * @since 0.1.0
- */
- public final List getElements() {
- return this.elements;
- }
-
- /**
- * Represents insertion of one or more subsequent {@link #getElements() elements} starting from a given
- * {@link #getIndex() index}.
- *
- * Example:
- *
- * Initial
- * Indices: 0 1 2 3 4 5
- * Elements: A B C D E F
- * ^
- * Insertion |
- * Index: 1
- * Elements: X Y Z
- *
- * Result
- * Indices: 0 1 2 3 4 5 6 7 8
- * Elements: A X Y Z B C D E F
- *
- *
- *
- * @since 0.1.0
- */
- public static final class Insertion extends LocalChange {
-
- Insertion(int index, List elements) {
- super(index, elements);
- }
-
- @Override
- LocalChange copy(Function super E, T> transform) {
- return new Insertion<>(this.getIndex(), this.getElements().stream().map(transform).toList());
- }
-
- }
-
- /**
- * Represents removal of one or more subsequent {@link #getElements() elements} starting from a given
- * {@link #getIndex() index}.
- *
- * Example:
- *
- * Initial
- * Indices: 0 1 2 3 4 5
- * Elements: A B C D E F
- * ^
- * Removal |
- * Index: 1
- * Elements: B C D
- *
- * Result
- * Indices: 0 1 2
- * Elements: A E F
- *
- *
- *
- * @since 0.1.0
- */
- public static final class Removal extends LocalChange {
-
- Removal(int index, List elements) {
- super(index, elements);
- }
-
- @Override
- LocalChange copy(Function super E, T> transform) {
- return new Removal<>(this.getIndex(), this.getElements().stream().map(transform).toList());
- }
-
- }
-
- /**
- * Represents an update of one or more subsequent {@link #getElements() elements} starting from a given
- * {@link #getIndex() index}.
- *
- * Example:
- *
- * Initial
- * Indices: 0 1 2 3 4 5
- * Elements: A B C D E F
- * ^
- * Update |
- * Index: 1
- * Elements: X Y Z
- *
- * Result
- * Indices: 0 1 2 3 4 5
- * Elements: A X Y Z E F
- *
- *
- *
- * @since 0.1.0
- */
- public static final class Update extends LocalChange {
-
- Update(int index, List elements) {
- super(index, elements);
- }
-
- @Override
- LocalChange copy(Function super E, T> transform) {
- return new Update<>(this.getIndex(), this.getElements().stream().map(transform).toList());
- }
-
- }
-
- }
-
}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableMap.java b/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableMap.java
index 7b57eb7..b4db32c 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableMap.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableMap.java
@@ -30,10 +30,9 @@
*/
package com.osmerion.quitte.collections;
-import java.util.Collections;
import java.util.Map;
-import javax.annotation.Nullable;
+import com.osmerion.quitte.Observable;
import com.osmerion.quitte.internal.collections.UnmodifiableObservableMap;
import com.osmerion.quitte.internal.collections.WrappingObservableMap;
@@ -47,7 +46,7 @@
*
* @author Leon Linhart
*/
-public interface ObservableMap extends Map, ObservableCollection> {
+public interface ObservableMap extends Map, Observable {
/**
* Returns an observable view of the specified map. Query operations on the returned map "read and write through"
@@ -90,64 +89,67 @@ static ObservableMap unmodifiableViewOf(ObservableMap map) {
}
/**
- * A change done to an {@link ObservableMap}.
+ * Attaches the given {@link MapChangeListener change listener} to this map.
*
- * @param the type of the map's elements
- * @param the type of the map's elements
+ * If the given listener is already attached to this map, this method does nothing and returns {@code false}.
*
- * @since 0.1.0
+ * While an {@code MapChangeListener} is attached to a map, it will be {@link MapChangeListener#onChanged(ObservableMap, MapChangeListener.Change)}
+ * notified} whenever the map is updated.
+ *
+ * This map stores a strong reference to the given listener until the listener is either removed explicitly by
+ * calling {@link #removeChangeListener(MapChangeListener)} or implicitly when this map discovers that the
+ * listener has become {@link MapChangeListener#isInvalid() invalid}. Generally, it is recommended to use an
+ * instance of {@link WeakMapChangeListener} when possible to avoid leaking instances.
+ *
+ * @param listener the listener to be attached to this map
+ *
+ * @return {@code true} if the listener was not previously attached to this map and has been successfully attached,
+ * or {@code false} otherwise
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @see #removeChangeListener(MapChangeListener)
+ *
+ * @since 0.8.0
*/
- record Change(
- Map addedElements,
- Map removedElements,
- Map> updatedElements
- ) {
+ boolean addChangeListener(MapChangeListener super K, ? super V> listener);
- public Change(@Nullable Map addedElements, @Nullable Map removedElements, @Nullable Map> updatedElements) {
- this.addedElements = (addedElements != null) ? Collections.unmodifiableMap(addedElements) : Collections.emptyMap();
- this.removedElements = (removedElements != null) ? Collections.unmodifiableMap(removedElements) : Collections.emptyMap();
- this.updatedElements = (updatedElements != null) ? Collections.unmodifiableMap(updatedElements) : Collections.emptyMap();
- }
-
- /**
- * Describes an update to a map entry's value.
- *
- * @since 0.1.0
- */
- public record Update(@Nullable V oldValue, @Nullable V newValue) {
-
- /**
- * Returns the old value.
- *
- * @return the old value
- *
- * @deprecated Deprecated in favor of canonical record accessor {@link #oldValue()}.
- *
- * @since 0.1.0
- */
- @Deprecated(since = "0.8.0", forRemoval = true)
- @Nullable
- public V getOldValue() {
- return this.oldValue;
- }
-
- /**
- * Returns the new value.
- *
- * @return the new value
- *
- * @deprecated Deprecated in favor of canonical record accessor {@link #newValue()}.
- *
- * @since 0.1.0
- */
- @Deprecated(since = "0.8.0", forRemoval = true)
- @Nullable
- public V getNewValue() {
- return this.newValue;
- }
+ /**
+ * Detaches the given {@link MapChangeListener change listener} from this map.
+ *
+ * If the given listener is not attached to this map, this method does nothing and returns {@code false}.
+ *
+ * @param listener the listener to be detached from this map
+ *
+ * @return {@code true} if the listener was attached to and has been detached from this map, or {@code false}
+ * otherwise
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @see #addChangeListener(MapChangeListener)
+ *
+ * @since 0.8.0
+ */
+ boolean removeChangeListener(MapChangeListener super K, ? super V> listener);
- }
+ /**
+ * {@inheritDoc}
+ *
+ * @return an observable set view of the mappings contained in this map
+ *
+ * @since 0.8.0
+ */
+ @Override
+ ObservableSet> entrySet();
- }
+ /**
+ * {@inheritDoc}
+ *
+ * @return an observable set view of the keys contained in this map
+ *
+ * @since 0.8.0
+ */
+ @Override
+ ObservableSet keySet();
}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableSet.java b/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableSet.java
index 173d881..f3c0f41 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableSet.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/collections/ObservableSet.java
@@ -30,9 +30,7 @@
*/
package com.osmerion.quitte.collections;
-import java.util.Collections;
import java.util.Set;
-import javax.annotation.Nullable;
import com.osmerion.quitte.internal.collections.UnmodifiableObservableSet;
import com.osmerion.quitte.internal.collections.WrappingObservableSet;
@@ -46,7 +44,7 @@
*
* @author Leon Linhart
*/
-public interface ObservableSet extends Set, ObservableCollection> {
+public interface ObservableSet extends Set, ObservableCollection {
/**
* Returns an observable view of the specified set. Query operations on the returned set "read and write through"
@@ -87,22 +85,47 @@ static ObservableSet unmodifiableViewOf(ObservableSet set) {
}
/**
- * A change done to an {@link ObservableSet}.
+ * Attaches the given {@link SetChangeListener change listener} to this set.
*
- * Note that adding an element that is already in a set does not modify the set and therefore no change will be
- * generated.
+ * If the given listener is already attached to this set, this method does nothing and returns {@code false}.
*
- * @param the type of the set's elements
+ * While an {@code SetChangeListener} is attached to a set, it will be {@link SetChangeListener#onChanged(ObservableSet, SetChangeListener.Change)}
+ * notified} whenever the set is updated.
*
- * @since 0.1.0
+ * This set stores a strong reference to the given listener until the listener is either removed explicitly by
+ * calling {@link #removeChangeListener(SetChangeListener)} or implicitly when this set discovers that the listener
+ * has become {@link SetChangeListener#isInvalid() invalid}. Generally, it is recommended to use an instance of
+ * {@link WeakSetChangeListener} when possible to avoid leaking instances.
+ *
+ * @param listener the listener to be attached to this set
+ *
+ * @return {@code true} if the listener was not previously attached to this set and has been successfully attached,
+ * or {@code false} otherwise
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @see #removeChangeListener(SetChangeListener)
+ *
+ * @since 0.8.0
*/
- record Change(Set addedElements, Set removedElements) {
+ boolean addChangeListener(SetChangeListener super E> listener);
- public Change(@Nullable Set addedElements, @Nullable Set removedElements) {
- this.addedElements = addedElements != null ? Collections.unmodifiableSet(addedElements) : Collections.emptySet();
- this.removedElements = removedElements != null ? Collections.unmodifiableSet(removedElements) : Collections.emptySet();
- }
-
- }
+ /**
+ * Detaches the given {@link SetChangeListener change listener} from this set.
+ *
+ * If the given listener is not attached to this set, this method does nothing and returns {@code false}.
+ *
+ * @param listener the listener to be detached from this set
+ *
+ * @return {@code true} if the listener was attached to and has been detached from this set, or {@code false}
+ * otherwise
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @see #addChangeListener(SetChangeListener)
+ *
+ * @since 0.8.0
+ */
+ boolean removeChangeListener(SetChangeListener super E> listener);
}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/collections/CollectionChangeListener.java b/modules/quitte/src/main/java/com/osmerion/quitte/collections/SetChangeListener.java
similarity index 54%
rename from modules/quitte/src/main/java/com/osmerion/quitte/collections/CollectionChangeListener.java
rename to modules/quitte/src/main/java/com/osmerion/quitte/collections/SetChangeListener.java
index e97a291..73bdf03 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/collections/CollectionChangeListener.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/collections/SetChangeListener.java
@@ -30,28 +30,32 @@
*/
package com.osmerion.quitte.collections;
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
/**
- * A listener that may be used to subscribe to changes to one or more observable collections.
- *
- * @param the type of change this listener can process
+ * A listener that may be used to subscribe to changes to one or more {@link ObservableSet observable set}.
*
- * @since 0.1.0
+ * @param the type of the set elements
*
- * @author Leon Linhart
+ * @since 0.8.0
*/
-public interface CollectionChangeListener {
+public interface SetChangeListener {
/**
- * Processes changes to an observable collection this listener is attached to.
+ * Processes changes to an {@link ObservableSet observable set} this listener is attached to.
*
- * @param change the change to process
+ * @param observable the observable set
+ * @param change the change to process
*
- * @since 0.1.0
+ * @since 0.8.0
*/
- void onChanged(C change);
+ void onChanged(ObservableSet extends E> observable, Change extends E> change);
/**
- * Returns whether this listener is invalid.
+ * {@return whether this listener is invalid}
*
* Once an observable collection discovers that a listener is invalid, it will stop notifying the listener of
* updates and release all strong references to the listener.
@@ -59,12 +63,40 @@ public interface CollectionChangeListener {
* Once this method returned {@code true}, it must never return {@code false} again for the same instance.
* Breaking this contract may result in unexpected behavior.
*
- * @return whether this listener is invalid
- *
* @since 0.1.0
*/
default boolean isInvalid() {
return false;
}
+ /**
+ * A change done to an {@link ObservableSet}.
+ *
+ * Note that adding an element that is already in a set does not modify the set and therefore no change will be
+ * generated.
+ *
+ * @param the type of the set's elements
+ * @param addedElements the added elements
+ * @param removedElements the removed elements
+ *
+ * @since 0.8.0
+ */
+ record Change(Set addedElements, Set removedElements) {
+
+ /**
+ * Creates a new {@code Change}.
+ *
+ * @param addedElements the added elements
+ * @param removedElements the removed elements
+ *
+ * @since 0.8.0
+ */
+ @SuppressWarnings("Java9CollectionFactory") // Cannot replace the code with Set::copyOf because the maps might contain null keys and values
+ public Change(@Nullable Set addedElements, @Nullable Set removedElements) {
+ this.addedElements = addedElements != null ? Collections.unmodifiableSet(new HashSet<>(addedElements)) : Set.of();
+ this.removedElements = removedElements != null ? Collections.unmodifiableSet(new HashSet<>(removedElements)) : Set.of();
+ }
+
+ }
+
}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakDequeChangeListener.java b/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakDequeChangeListener.java
new file mode 100644
index 0000000..297c102
--- /dev/null
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakDequeChangeListener.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018-2023 Leon Linhart,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.osmerion.quitte.collections;
+
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+
+/**
+ * A {@code WeakDequeChangeListener} may be used to wrap a listener that should only be referenced weakly from an
+ * {@link ObservableDeque}.
+ *
+ * This listener does not keep a strong reference to the wrapped listener.
+ *
+ * @param the type of the deque elements
+ *
+ * @see WeakReference
+ *
+ * @since 0.8.0
+ *
+ * @author Leon Linhart
+ */
+public final class WeakDequeChangeListener implements DequeChangeListener {
+
+ private final WeakReference> ref;
+
+ private boolean wasGarbageCollected;
+
+ /**
+ * Wraps the given {@link DequeChangeListener listener}.
+ *
+ * @param listener the listener to wrap
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @since 0.8.0
+ */
+ public WeakDequeChangeListener(DequeChangeListener listener) {
+ this.ref = new WeakReference<>(Objects.requireNonNull(listener));
+ this.wasGarbageCollected = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 0.8.0
+ */
+ @Override
+ public void onChanged(ObservableDeque extends E> observable, DequeChangeListener.Change extends E> change) {
+ var listener = this.ref.get();
+
+ if (listener != null) {
+ listener.onChanged(observable, change);
+ } else {
+ this.wasGarbageCollected = true;
+ }
+ }
+
+ /**
+ * {@return whether the underlying listener was garbage collected or has become invalid}
+ *
+ * @since 0.8.0
+ */
+ @Override
+ public boolean isInvalid() {
+ if (this.wasGarbageCollected) return true;
+
+ var listener = this.ref.get();
+ return (listener != null && listener.isInvalid());
+ }
+
+}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakListChangeListener.java b/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakListChangeListener.java
new file mode 100644
index 0000000..e12424b
--- /dev/null
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakListChangeListener.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018-2023 Leon Linhart,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.osmerion.quitte.collections;
+
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+
+/**
+ * A {@code WeakListChangeListener} may be used to wrap a listener that should only be referenced weakly from an
+ * {@link ObservableList}.
+ *
+ * This listener does not keep a strong reference to the wrapped listener.
+ *
+ * @param the type of the list elements
+ *
+ * @see WeakReference
+ *
+ * @since 0.8.0
+ *
+ * @author Leon Linhart
+ */
+public final class WeakListChangeListener implements ListChangeListener {
+
+ private final WeakReference> ref;
+
+ private boolean wasGarbageCollected;
+
+ /**
+ * Wraps the given {@link ListChangeListener listener}.
+ *
+ * @param listener the listener to wrap
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @since 0.8.0
+ */
+ public WeakListChangeListener(ListChangeListener listener) {
+ this.ref = new WeakReference<>(Objects.requireNonNull(listener));
+ this.wasGarbageCollected = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 0.8.0
+ */
+ @Override
+ public void onChanged(ObservableList extends E> observable, ListChangeListener.Change extends E> change) {
+ var listener = this.ref.get();
+
+ if (listener != null) {
+ listener.onChanged(observable, change);
+ } else {
+ this.wasGarbageCollected = true;
+ }
+ }
+
+ /**
+ * {@return whether the underlying listener was garbage collected or has become invalid}
+ *
+ * @since 0.8.0
+ */
+ @Override
+ public boolean isInvalid() {
+ if (this.wasGarbageCollected) return true;
+
+ var listener = this.ref.get();
+ return (listener != null && listener.isInvalid());
+ }
+
+}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakMapChangeListener.java b/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakMapChangeListener.java
new file mode 100644
index 0000000..78a8ca7
--- /dev/null
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakMapChangeListener.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2018-2023 Leon Linhart,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.osmerion.quitte.collections;
+
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+
+/**
+ * A {@code WeakMapChangeListener} may be used to wrap a listener that should only be referenced weakly from an
+ * {@link ObservableMap}.
+ *
+ * This listener does not keep a strong reference to the wrapped listener.
+ *
+ * @param the type of the map keys
+ * @param the type of the map values
+ *
+ * @see WeakReference
+ *
+ * @since 0.8.0
+ *
+ * @author Leon Linhart
+ */
+public final class WeakMapChangeListener implements MapChangeListener {
+
+ private final WeakReference> ref;
+
+ private boolean wasGarbageCollected;
+
+ /**
+ * Wraps the given {@link MapChangeListener listener}.
+ *
+ * @param listener the listener to wrap
+ *
+ * @throws NullPointerException if the given listener is {@code null}
+ *
+ * @since 0.8.0
+ */
+ public WeakMapChangeListener(MapChangeListener listener) {
+ this.ref = new WeakReference<>(Objects.requireNonNull(listener));
+ this.wasGarbageCollected = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 0.8.0
+ */
+ @Override
+ public void onChanged(ObservableMap extends K, ? extends V> observable, MapChangeListener.Change extends K, ? extends V> change) {
+ var listener = this.ref.get();
+
+ if (listener != null) {
+ listener.onChanged(observable, change);
+ } else {
+ this.wasGarbageCollected = true;
+ }
+ }
+
+ /**
+ * {@return whether the underlying listener was garbage collected or has become invalid}
+ *
+ * @since 0.8.0
+ */
+ @Override
+ public boolean isInvalid() {
+ if (this.wasGarbageCollected) return true;
+
+ var listener = this.ref.get();
+ return (listener != null && listener.isInvalid());
+ }
+
+}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakCollectionChangeListener.java b/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakSetChangeListener.java
similarity index 74%
rename from modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakCollectionChangeListener.java
rename to modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakSetChangeListener.java
index 47d2bac..269ad3b 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakCollectionChangeListener.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/collections/WeakSetChangeListener.java
@@ -34,45 +34,50 @@
import java.util.Objects;
/**
- * A {@code WeakCollectionChangeListener} may be used to wrap a listener that should only be referenced weakly from an
- * {@link ObservableCollection}.
+ * A {@code WeakSetChangeListener} may be used to wrap a listener that should only be referenced weakly from an
+ * {@link ObservableSet}.
+ *
+ * This listener does not keep a strong reference to the wrapped listener.
+ *
+ * @param the type of the set elements
*
* @see WeakReference
*
- * @since 0.1.0
+ * @since 0.8.0
*
* @author Leon Linhart
*/
-public final class WeakCollectionChangeListener implements CollectionChangeListener {
+public final class WeakSetChangeListener implements SetChangeListener {
- private final WeakReference> ref;
+ private final WeakReference> ref;
private boolean wasGarbageCollected;
/**
- * Wraps the given {@link CollectionChangeListener listener}.
+ * Wraps the given {@link SetChangeListener listener}.
*
* @param listener the listener to wrap
*
* @throws NullPointerException if the given listener is {@code null}
*
- * @since 0.1.0
+ * @since 0.8.0
*/
- public WeakCollectionChangeListener(CollectionChangeListener listener) {
+ public WeakSetChangeListener(SetChangeListener listener) {
this.ref = new WeakReference<>(Objects.requireNonNull(listener));
+ this.wasGarbageCollected = false;
}
/**
* {@inheritDoc}
*
- * @since 0.1.0
+ * @since 0.8.0
*/
@Override
- public void onChanged(C change) {
- CollectionChangeListener listener = this.ref.get();
+ public void onChanged(ObservableSet extends E> observable, SetChangeListener.Change extends E> change) {
+ var listener = this.ref.get();
if (listener != null) {
- listener.onChanged(change);
+ listener.onChanged(observable, change);
} else {
this.wasGarbageCollected = true;
}
@@ -81,7 +86,7 @@ public void onChanged(C change) {
/**
* {@return whether the underlying listener was garbage collected or has become invalid}
*
- * @since 0.1.0
+ * @since 0.8.0
*/
@Override
public boolean isInvalid() {
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/DequeBinding.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/DequeBinding.java
index 74fb8ee..b18f498 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/DequeBinding.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/DequeBinding.java
@@ -30,18 +30,15 @@
*/
package com.osmerion.quitte.internal.binding;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
import java.util.function.Function;
+import java.util.stream.Collectors;
import com.osmerion.quitte.InvalidationListener;
import com.osmerion.quitte.WeakInvalidationListener;
-import com.osmerion.quitte.collections.CollectionChangeListener;
+import com.osmerion.quitte.collections.DequeChangeListener;
import com.osmerion.quitte.collections.ObservableDeque;
-import com.osmerion.quitte.collections.WeakCollectionChangeListener;
+import com.osmerion.quitte.collections.WeakDequeChangeListener;
/**
* A specialized {@link Deque} binding.
@@ -50,12 +47,12 @@
*/
public final class DequeBinding implements Binding {
- private final Deque> changes = new ArrayDeque<>();
+ private final Deque> changes = new ArrayDeque<>();
private final ObservableDeque source;
private final InvalidationListener invalidationListener;
- private final CollectionChangeListener> changeListener;
+ private final DequeChangeListener changeListener;
private final Function super S, E> transform;
@@ -64,18 +61,39 @@ public DequeBinding(Runnable invalidator, ObservableDeque source, Function
this.transform = transform;
this.source.addInvalidationListener(new WeakInvalidationListener(this.invalidationListener = (observable) -> invalidator.run()));
- this.source.addChangeListener(new WeakCollectionChangeListener<>(this.changeListener = this.changes::addLast));
+ this.source.addChangeListener(new WeakDequeChangeListener<>(this.changeListener = ((observable, change) -> this.changes.addLast(change))));
}
- @SuppressWarnings("deprecation")
- public List> getChanges() {
- List> changes = new ArrayList<>(this.changes.size());
- Iterator> changeItr = this.changes.iterator();
+ public List> getChanges() {
+ List> changes = new ArrayList<>(this.changes.size());
+ Iterator> changeItr = this.changes.iterator();
while (changeItr.hasNext()) {
- ObservableDeque.Change extends S> change = changeItr.next();
- changes.add(change.copy(this.transform));
+ DequeChangeListener.Change extends S> change = changeItr.next();
changeItr.remove();
+
+ DequeChangeListener.Change transformedChange = new DequeChangeListener.Change<>(
+ change.localChanges().stream()
+ .map(it -> {
+ if (it instanceof DequeChangeListener.LocalChange.Insertion extends S> localInsertion) {
+ //noinspection SimplifyStreamApiCallChains
+ return new DequeChangeListener.LocalChange.Insertion<>(
+ localInsertion.site(),
+ localInsertion.elements().stream().map(this.transform).collect(Collectors.toUnmodifiableList())
+ );
+ } else if (it instanceof DequeChangeListener.LocalChange.Removal extends S> localRemoval) {
+ //noinspection SimplifyStreamApiCallChains
+ return new DequeChangeListener.LocalChange.Removal<>(
+ localRemoval.site(),
+ localRemoval.elements().stream().map(this.transform).collect(Collectors.toUnmodifiableList())
+ );
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+ ).toList());
+
+ changes.add(transformedChange);
}
return changes;
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/ListBinding.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/ListBinding.java
index 883e925..6e6c3bb 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/ListBinding.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/ListBinding.java
@@ -36,12 +36,13 @@
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
+import java.util.stream.Collectors;
import com.osmerion.quitte.InvalidationListener;
import com.osmerion.quitte.WeakInvalidationListener;
-import com.osmerion.quitte.collections.CollectionChangeListener;
+import com.osmerion.quitte.collections.ListChangeListener;
import com.osmerion.quitte.collections.ObservableList;
-import com.osmerion.quitte.collections.WeakCollectionChangeListener;
+import com.osmerion.quitte.collections.WeakListChangeListener;
/**
* A specialized {@link List} binding.
@@ -50,12 +51,12 @@
*/
public final class ListBinding implements Binding {
- private final Deque> changes = new ArrayDeque<>();
+ private final Deque> changes = new ArrayDeque<>();
private final ObservableList source;
private final InvalidationListener invalidationListener;
- private final CollectionChangeListener> changeListener;
+ private final ListChangeListener changeListener;
private final Function super S, E> transform;
@@ -64,18 +65,55 @@ public ListBinding(Runnable invalidator, ObservableList source, Function su
this.transform = transform;
this.source.addInvalidationListener(new WeakInvalidationListener(this.invalidationListener = (observable) -> invalidator.run()));
- this.source.addChangeListener(new WeakCollectionChangeListener<>(this.changeListener = this.changes::addLast));
+ this.source.addChangeListener(new WeakListChangeListener<>(this.changeListener = (observable, change) -> this.changes.addLast(change)));
}
- @SuppressWarnings("deprecation")
- public List> getChanges() {
- List> changes = new ArrayList<>(this.changes.size());
- Iterator> changeItr = this.changes.iterator();
+ public List> getChanges() {
+ List> changes = new ArrayList<>(this.changes.size());
+ Iterator> changeItr = this.changes.iterator();
while (changeItr.hasNext()) {
- ObservableList.Change extends S> change = changeItr.next();
- changes.add(change.copy(this.transform));
+ ListChangeListener.Change extends S> change = changeItr.next();
changeItr.remove();
+
+ ListChangeListener.Change transformedChange;
+
+ if (change instanceof ListChangeListener.Change.Permutation extends S> permutation) {
+ transformedChange = new ListChangeListener.Change.Permutation<>(permutation.indices());
+ } else if (change instanceof ListChangeListener.Change.Update extends S> update) {
+ List extends ListChangeListener.LocalChange> transformedLocalChanges = update.localChanges().stream()
+ .map(it -> {
+ if (it instanceof ListChangeListener.LocalChange.Insertion extends S> localInsertion) {
+ //noinspection SimplifyStreamApiCallChains
+ return new ListChangeListener.LocalChange.Insertion<>(
+ localInsertion.index(),
+ localInsertion.elements().stream().map(this.transform).collect(Collectors.toUnmodifiableList())
+ );
+ } else if (it instanceof ListChangeListener.LocalChange.Removal extends S> localRemoval) {
+ //noinspection SimplifyStreamApiCallChains
+ return new ListChangeListener.LocalChange.Removal<>(
+ localRemoval.index(),
+ localRemoval.elements().stream().map(this.transform).collect(Collectors.toUnmodifiableList())
+ );
+ } else if (it instanceof ListChangeListener.LocalChange.Update extends S> localUpdate) {
+ //noinspection SimplifyStreamApiCallChains
+ return new ListChangeListener.LocalChange.Update<>(
+ localUpdate.index(),
+ localUpdate.oldElements().stream().map(this.transform).collect(Collectors.toUnmodifiableList()),
+ localUpdate.newElements().stream().map(this.transform).collect(Collectors.toUnmodifiableList())
+ );
+ } else {
+ throw new IllegalStateException();
+ }
+ })
+ .toList();
+
+ transformedChange = new ListChangeListener.Change.Update<>(transformedLocalChanges);
+ } else {
+ throw new IllegalStateException();
+ }
+
+ changes.add(transformedChange);
}
return changes;
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/MapBinding.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/MapBinding.java
index f00c2ac..48dc434 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/MapBinding.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/MapBinding.java
@@ -41,9 +41,9 @@
import com.osmerion.quitte.InvalidationListener;
import com.osmerion.quitte.WeakInvalidationListener;
-import com.osmerion.quitte.collections.CollectionChangeListener;
+import com.osmerion.quitte.collections.MapChangeListener;
import com.osmerion.quitte.collections.ObservableMap;
-import com.osmerion.quitte.collections.WeakCollectionChangeListener;
+import com.osmerion.quitte.collections.WeakMapChangeListener;
/**
* A specialized {@link Map} binding.
@@ -52,12 +52,12 @@
*/
public final class MapBinding implements Binding {
- private final Deque> changes = new ArrayDeque<>();
+ private final Deque> changes = new ArrayDeque<>();
private final ObservableMap source;
private final InvalidationListener invalidationListener;
- private final CollectionChangeListener> changeListener;
+ private final MapChangeListener changeListener;
private final BiFunction super S, ? super T, Map.Entry> transform;
@@ -66,25 +66,25 @@ public MapBinding(Runnable invalidator, ObservableMap source, BiFunction
this.transform = transform;
this.source.addInvalidationListener(new WeakInvalidationListener(this.invalidationListener = (observable) -> invalidator.run()));
- this.source.addChangeListener(new WeakCollectionChangeListener<>(this.changeListener = this.changes::addLast));
+ this.source.addChangeListener(new WeakMapChangeListener<>(this.changeListener = (observable, change) -> this.changes.addLast(change)));
}
- public List> getChanges() {
- List> changes = new ArrayList<>(this.changes.size());
- Iterator> changeItr = this.changes.iterator();
+ public List> getChanges() {
+ List> changes = new ArrayList<>(this.changes.size());
+ Iterator> changeItr = this.changes.iterator();
while (changeItr.hasNext()) {
- ObservableMap.Change extends S, ? extends T> change = changeItr.next();
+ MapChangeListener.Change extends S, ? extends T> change = changeItr.next();
changeItr.remove();
- ObservableMap.Change transformedChange = new ObservableMap.Change<>(
+ MapChangeListener.Change transformedChange = new MapChangeListener.Change<>(
change.addedElements().entrySet().stream().map(e -> this.transform.apply(e.getKey(), e.getValue())).collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)),
change.removedElements().entrySet().stream().map(e -> this.transform.apply(e.getKey(), e.getValue())).collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)),
change.updatedElements().entrySet().stream().map(e -> {
Map.Entry oldValue = this.transform.apply(e.getKey(), e.getValue().oldValue());
Map.Entry newValue = this.transform.apply(e.getKey(), e.getValue().newValue());
- return Map.entry(newValue.getKey(), new ObservableMap.Change.Update<>(oldValue.getValue(), newValue.getValue()));
+ return Map.entry(newValue.getKey(), new MapChangeListener.Change.Update<>(oldValue.getValue(), newValue.getValue()));
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
);
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/SetBinding.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/SetBinding.java
index fc54116..9947cc2 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/SetBinding.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/binding/SetBinding.java
@@ -41,9 +41,9 @@
import com.osmerion.quitte.InvalidationListener;
import com.osmerion.quitte.WeakInvalidationListener;
-import com.osmerion.quitte.collections.CollectionChangeListener;
import com.osmerion.quitte.collections.ObservableSet;
-import com.osmerion.quitte.collections.WeakCollectionChangeListener;
+import com.osmerion.quitte.collections.SetChangeListener;
+import com.osmerion.quitte.collections.WeakSetChangeListener;
/**
* A specialized {@link Set} binding.
@@ -52,12 +52,12 @@
*/
public final class SetBinding implements Binding {
- private final Deque> changes = new ArrayDeque<>();
+ private final Deque> changes = new ArrayDeque<>();
private final ObservableSet source;
private final InvalidationListener invalidationListener;
- private final CollectionChangeListener> changeListener;
+ private final SetChangeListener changeListener;
private final Function transform;
@@ -66,18 +66,18 @@ public SetBinding(Runnable invalidator, ObservableSet source, Function
this.transform = transform;
this.source.addInvalidationListener(new WeakInvalidationListener(this.invalidationListener = (observable) -> invalidator.run()));
- this.source.addChangeListener(new WeakCollectionChangeListener<>(this.changeListener = this.changes::addLast));
+ this.source.addChangeListener(new WeakSetChangeListener<>(this.changeListener = ((observable, change) -> this.changes.addLast(change))));
}
- public List> getChanges() {
- List> changes = new ArrayList<>(this.changes.size());
- Iterator> changeItr = this.changes.iterator();
+ public List> getChanges() {
+ List> changes = new ArrayList<>(this.changes.size());
+ Iterator> changeItr = this.changes.iterator();
while (changeItr.hasNext()) {
- ObservableSet.Change extends S> change = changeItr.next();
+ SetChangeListener.Change extends S> change = changeItr.next();
changeItr.remove();
- ObservableSet.Change transformedChange = new ObservableSet.Change<>(
+ SetChangeListener.Change transformedChange = new SetChangeListener.Change<>(
change.addedElements().stream().map(this.transform).collect(Collectors.toUnmodifiableSet()),
change.removedElements().stream().map(this.transform).collect(Collectors.toUnmodifiableSet())
);
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableDeque.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableDeque.java
index 03b24ed..14ba51e 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableDeque.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableDeque.java
@@ -36,7 +36,7 @@
import javax.annotation.Nullable;
import com.osmerion.quitte.InvalidationListener;
-import com.osmerion.quitte.collections.CollectionChangeListener;
+import com.osmerion.quitte.collections.DequeChangeListener;
import com.osmerion.quitte.collections.ObservableDeque;
/**
@@ -63,8 +63,8 @@ public UnmodifiableObservableDeque(ObservableDeque impl) {
@Override public boolean addInvalidationListener(InvalidationListener listener) { return this.impl.addInvalidationListener(listener); }
@Override public boolean removeInvalidationListener(InvalidationListener listener) { return this.impl.removeInvalidationListener(listener); }
- @Override public boolean addChangeListener(CollectionChangeListener super ObservableDeque.Change extends E>> listener) { return this.impl.addChangeListener(listener); }
- @Override public boolean removeChangeListener(CollectionChangeListener super ObservableDeque.Change extends E>> listener) { return this.impl.removeChangeListener(listener); }
+ @Override public boolean addChangeListener(DequeChangeListener super E> listener) { return this.impl.addChangeListener(listener); }
+ @Override public boolean removeChangeListener(DequeChangeListener super E> listener) { return this.impl.removeChangeListener(listener); }
@Override public boolean contains(Object o) { return this.impl.contains(o); }
@Override public boolean containsAll(Collection> c) { return this.impl.containsAll(c); }
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableList.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableList.java
index a7b3f73..ff6a95e 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableList.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableList.java
@@ -34,7 +34,7 @@
import java.util.Collection;
import com.osmerion.quitte.InvalidationListener;
-import com.osmerion.quitte.collections.CollectionChangeListener;
+import com.osmerion.quitte.collections.ListChangeListener;
import com.osmerion.quitte.collections.ObservableList;
/**
@@ -61,8 +61,8 @@ public UnmodifiableObservableList(ObservableList impl) {
@Override public boolean addInvalidationListener(InvalidationListener listener) { return this.impl.addInvalidationListener(listener); }
@Override public boolean removeInvalidationListener(InvalidationListener listener) { return this.impl.removeInvalidationListener(listener); }
- @Override public boolean addChangeListener(CollectionChangeListener super Change extends E>> listener) { return this.impl.addChangeListener(listener); }
- @Override public boolean removeChangeListener(CollectionChangeListener super ObservableList.Change extends E>> listener) { return this.impl.removeChangeListener(listener); }
+ @Override public boolean addChangeListener(ListChangeListener super E> listener) { return this.impl.addChangeListener(listener); }
+ @Override public boolean removeChangeListener(ListChangeListener super E> listener) { return this.impl.removeChangeListener(listener); }
@Override public boolean contains(Object o) { return this.impl.contains(o); }
@Override public boolean containsAll(Collection> c) { return this.impl.containsAll(c); }
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableMap.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableMap.java
index 753d11e..e5d2d19 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableMap.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableMap.java
@@ -34,12 +34,12 @@
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import javax.annotation.Nullable;
import com.osmerion.quitte.InvalidationListener;
-import com.osmerion.quitte.collections.CollectionChangeListener;
+import com.osmerion.quitte.collections.MapChangeListener;
import com.osmerion.quitte.collections.ObservableMap;
+import com.osmerion.quitte.collections.ObservableSet;
/**
* A wrapper for an {@link ObservableMap} that blocks mutation.
@@ -66,18 +66,18 @@ public UnmodifiableObservableMap(ObservableMap impl) {
@Override public boolean addInvalidationListener(InvalidationListener listener) { return this.impl.addInvalidationListener(listener); }
@Override public boolean removeInvalidationListener(InvalidationListener listener) { return this.impl.removeInvalidationListener(listener); }
- @Override public boolean addChangeListener(CollectionChangeListener super Change extends K, ? extends V>> listener) { return this.impl.addChangeListener(listener); }
- @Override public boolean removeChangeListener(CollectionChangeListener super Change extends K, ? extends V>> listener) { return this.impl.removeChangeListener(listener); }
+ @Override public boolean addChangeListener(MapChangeListener super K, ? super V> listener) { return this.impl.addChangeListener(listener); }
+ @Override public boolean removeChangeListener(MapChangeListener super K, ? super V> listener) { return this.impl.removeChangeListener(listener); }
@Override public boolean containsKey(Object key) { return this.impl.containsKey(key); }
@Override public boolean containsValue(Object value) { return this.impl.containsValue(value); }
@Nullable
- private transient Set> entrySet;
+ private transient ObservableSet> entrySet;
@Override
- public Set> entrySet() {
- if (this.entrySet == null) this.entrySet = Collections.unmodifiableSet(this.impl.entrySet());
+ public ObservableSet> entrySet() {
+ if (this.entrySet == null) this.entrySet = ObservableSet.unmodifiableViewOf(this.impl.entrySet());
return this.entrySet;
}
@@ -86,8 +86,8 @@ public Set> entrySet() {
@Override public boolean isEmpty() { return this.impl.isEmpty(); }
@Override
- public Set keySet() {
- return Collections.unmodifiableSet(this.impl.keySet());
+ public ObservableSet keySet() {
+ return ObservableSet.unmodifiableViewOf(this.impl.keySet());
}
@Override public int size() { return this.impl.size(); }
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableSet.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableSet.java
index 637f423..96cb815 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableSet.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/UnmodifiableObservableSet.java
@@ -35,8 +35,8 @@
import java.util.Objects;
import com.osmerion.quitte.InvalidationListener;
-import com.osmerion.quitte.collections.CollectionChangeListener;
import com.osmerion.quitte.collections.ObservableSet;
+import com.osmerion.quitte.collections.SetChangeListener;
/**
* A wrapper for an {@link ObservableSet} that blocks mutation.
@@ -62,8 +62,8 @@ public UnmodifiableObservableSet(ObservableSet impl) {
@Override public boolean addInvalidationListener(InvalidationListener listener) { return this.impl.addInvalidationListener(listener); }
@Override public boolean removeInvalidationListener(InvalidationListener listener) { return this.impl.removeInvalidationListener(listener); }
- @Override public boolean addChangeListener(CollectionChangeListener super Change extends E>> listener) { return this.impl.addChangeListener(listener); }
- @Override public boolean removeChangeListener(CollectionChangeListener super ObservableSet.Change extends E>> listener) { return this.impl.removeChangeListener(listener); }
+ @Override public boolean addChangeListener(SetChangeListener super E> listener) { return this.impl.addChangeListener(listener); }
+ @Override public boolean removeChangeListener(SetChangeListener super E> listener) { return this.impl.removeChangeListener(listener); }
@Override public boolean contains(Object o) { return this.impl.contains(o); }
@Override public boolean containsAll(Collection> c) { return this.impl.containsAll(c); }
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/WrappingObservableDeque.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/WrappingObservableDeque.java
index 1e572bc..03dbf19 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/WrappingObservableDeque.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/WrappingObservableDeque.java
@@ -35,6 +35,7 @@
import javax.annotation.Nullable;
import com.osmerion.quitte.collections.AbstractObservableDeque;
+import com.osmerion.quitte.collections.DequeChangeListener;
import com.osmerion.quitte.collections.ObservableDeque;
/**
@@ -91,7 +92,7 @@ public E next() {
public void remove() {
try (ChangeBuilder changeBuilder = WrappingObservableDeque.this.beginChange()) {
this.impl.remove();
- changeBuilder.logRemove(ObservableDeque.Site.OPAQUE, this.cursor);
+ changeBuilder.logRemove(DequeChangeListener.Site.OPAQUE, this.cursor);
}
}
@@ -122,7 +123,7 @@ public E next() {
public void remove() {
try (ChangeBuilder changeBuilder = WrappingObservableDeque.this.beginChange()) {
this.impl.remove();
- changeBuilder.logRemove(ObservableDeque.Site.OPAQUE, this.cursor);
+ changeBuilder.logRemove(DequeChangeListener.Site.OPAQUE, this.cursor);
}
}
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/WrappingObservableMap.java b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/WrappingObservableMap.java
index 90b2ccf..de4704d 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/WrappingObservableMap.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/internal/collections/WrappingObservableMap.java
@@ -56,19 +56,11 @@ public final class WrappingObservableMap extends AbstractObservableMap impl;
- @Nullable
- private transient Set> entrySet;
-
public WrappingObservableMap(Map impl) {
this.impl = Objects.requireNonNull(impl);
}
+ @Override protected Set> entrySetImpl() { return this.impl.entrySet(); }
@Override public V putImpl(@Nullable K key, @Nullable V value) { return this.impl.put(key, value); }
- @Override
- public Set> entrySet() {
- if (this.entrySet == null) this.entrySet = new WrappingObservableEntrySet(this.impl.entrySet());
- return this.entrySet;
- }
-
}
\ No newline at end of file
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/property/DequeProperty.java b/modules/quitte/src/main/java/com/osmerion/quitte/property/DequeProperty.java
index 11e0c75..32c9671 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/property/DequeProperty.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/property/DequeProperty.java
@@ -38,6 +38,7 @@
import javax.annotation.Nullable;
import com.osmerion.quitte.collections.AbstractObservableDeque;
+import com.osmerion.quitte.collections.DequeChangeListener;
import com.osmerion.quitte.collections.ObservableDeque;
import com.osmerion.quitte.internal.binding.DequeBinding;
@@ -272,7 +273,7 @@ public void remove() {
try (ChangeBuilder changeBuilder = DequeProperty.this.beginChange()) {
this.impl.remove();
- changeBuilder.logRemove(ObservableDeque.Site.OPAQUE, this.cursor);
+ changeBuilder.logRemove(DequeChangeListener.Site.OPAQUE, this.cursor);
}
}
@@ -328,7 +329,7 @@ public void remove() {
try (ChangeBuilder changeBuilder = DequeProperty.this.beginChange()) {
this.impl.remove();
- changeBuilder.logRemove(ObservableDeque.Site.OPAQUE, this.cursor);
+ changeBuilder.logRemove(DequeChangeListener.Site.OPAQUE, this.cursor);
}
}
@@ -378,7 +379,7 @@ public final int size() {
void onBindingInvalidated() {
assert (this.binding != null);
- List> changes = this.binding.getChanges();
+ List> changes = this.binding.getChanges();
try {
this.inBoundUpdate = true;
@@ -386,18 +387,18 @@ void onBindingInvalidated() {
try (ChangeBuilder ignored = this.beginChange()) {
for (var change : changes) {
for (var localChange : change.localChanges()) {
- if (localChange instanceof LocalChange.Insertion insertion) {
- switch (insertion.getSite()) {
- case HEAD -> insertion.getElements().forEach(this::addFirst);
- case TAIL -> insertion.getElements().forEach(this::addLast);
+ if (localChange instanceof DequeChangeListener.LocalChange.Insertion insertion) {
+ switch (insertion.site()) {
+ case HEAD -> insertion.elements().forEach(this::addFirst);
+ case TAIL -> insertion.elements().forEach(this::addLast);
default -> throw new IllegalStateException();
}
- } else if (localChange instanceof LocalChange.Removal removal) {
- switch (removal.getSite()) {
- case HEAD -> removal.getElements().forEach(it -> this.removeFirst());
- case TAIL -> removal.getElements().forEach(it -> this.removeLast());
+ } else if (localChange instanceof DequeChangeListener.LocalChange.Removal removal) {
+ switch (removal.site()) {
+ case HEAD -> removal.elements().forEach(it -> this.removeFirst());
+ case TAIL -> removal.elements().forEach(it -> this.removeLast());
// TODO approximation might produce incorrect results, maybe we should disallow operations that produce opaque changes for bound observable deques?
- case OPAQUE -> removal.getElements().forEach(this::remove);
+ case OPAQUE -> removal.elements().forEach(this::remove);
default -> throw new IllegalStateException();
}
} else {
diff --git a/modules/quitte/src/main/java/com/osmerion/quitte/property/ListProperty.java b/modules/quitte/src/main/java/com/osmerion/quitte/property/ListProperty.java
index af42fd5..e6c8877 100644
--- a/modules/quitte/src/main/java/com/osmerion/quitte/property/ListProperty.java
+++ b/modules/quitte/src/main/java/com/osmerion/quitte/property/ListProperty.java
@@ -38,6 +38,7 @@
import javax.annotation.Nullable;
import com.osmerion.quitte.collections.AbstractObservableList;
+import com.osmerion.quitte.collections.ListChangeListener;
import com.osmerion.quitte.collections.ObservableList;
import com.osmerion.quitte.internal.binding.ListBinding;
@@ -218,15 +219,15 @@ public final int size() {
void onBindingInvalidated() {
assert (this.binding != null);
- List> changes = this.binding.getChanges();
+ List> changes = this.binding.getChanges();
try {
this.inBoundUpdate = true;
try (ChangeBuilder ignored = this.beginChange()) {
for (var change : changes) {
- if (change instanceof Change.Permutation perm) {
- List indices = perm.getIndices();
+ if (change instanceof ListChangeListener.Change.Permutation perm) {
+ List indices = perm.indices();
if (this.size() != indices.size()) throw new IndexOutOfBoundsException();
@@ -235,31 +236,31 @@ void onBindingInvalidated() {
for (int i = 0; i < this.size(); i++) {
this.set(indices.get(i), copy.get(i));
}
- } else if (change instanceof Change.Update update) {
- for (var localChange : update.getLocalChanges()) {
- if (localChange instanceof LocalChange.Insertion insertion) {
- if (this.size() < insertion.getIndex()) throw new IndexOutOfBoundsException();
+ } else if (change instanceof ListChangeListener.Change.Update