Skip to content

Commit

Permalink
feat(quitte): refactor collection listeners
Browse files Browse the repository at this point in the history
Closes #10
Closes #11
  • Loading branch information
TheMrMilchmann committed Aug 10, 2023
1 parent b9955fe commit b6b2c49
Show file tree
Hide file tree
Showing 40 changed files with 1,741 additions and 958 deletions.
21 changes: 6 additions & 15 deletions docs/changelog/0.8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -80,7 +82,7 @@ public fun <T> ObservableValue<T>.asFlow(): Flow<T> = callbackFlow {
* @since 0.3.0
*/
public fun <E> ObservableList<E>.asFlow(): Flow<List<E>> = callbackFlow {
val listener = CollectionChangeListener<ObservableList.Change<out E>> {
val listener = ListChangeListener<E> { _, _ ->
trySend(this@asFlow.toList())
}

Expand All @@ -103,7 +105,7 @@ public fun <E> ObservableList<E>.asFlow(): Flow<List<E>> = callbackFlow {
* @since 0.3.0
*/
public fun <K, V> ObservableMap<K, V>.asFlow(): Flow<Map<K, V>> = callbackFlow {
val listener = CollectionChangeListener<ObservableMap.Change<out K, out V>> {
val listener = MapChangeListener<K, V> { _, _ ->
trySend(this@asFlow.toMap())
}

Expand All @@ -126,7 +128,7 @@ public fun <K, V> ObservableMap<K, V>.asFlow(): Flow<Map<K, V>> = callbackFlow {
* @since 0.3.0
*/
public fun <E> ObservableSet<E>.asFlow(): Flow<Set<E>> = callbackFlow {
val listener = CollectionChangeListener<ObservableSet.Change<out E>> {
val listener = SetChangeListener<E> { _, _ ->
trySend(this@asFlow.toSet())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,7 @@
*/
package com.osmerion.quitte.collections;

import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.Nullable;

Expand All @@ -53,7 +47,7 @@
*/
public abstract class AbstractObservableDeque<E> extends AbstractCollection<E> implements ObservableDeque<E> {

private transient final CopyOnWriteArraySet<CollectionChangeListener<? super ObservableDeque.Change<? extends E>>> changeListeners = new CopyOnWriteArraySet<>();
private transient final CopyOnWriteArraySet<DequeChangeListener<? super E>> changeListeners = new CopyOnWriteArraySet<>();
private transient final CopyOnWriteArraySet<InvalidationListener> invalidationListeners = new CopyOnWriteArraySet<>();

@Nullable
Expand All @@ -65,7 +59,7 @@ public abstract class AbstractObservableDeque<E> extends AbstractCollection<E> i
* @since 0.1.0
*/
@Override
public final boolean addChangeListener(CollectionChangeListener<? super ObservableDeque.Change<? extends E>> listener) {
public final boolean addChangeListener(DequeChangeListener<? super E> listener) {
return this.changeListeners.add(Objects.requireNonNull(listener));
}

Expand All @@ -75,7 +69,7 @@ public final boolean addChangeListener(CollectionChangeListener<? super Observab
* @since 0.1.0
*/
@Override
public final boolean removeChangeListener(CollectionChangeListener<? super ObservableDeque.Change<? extends E>> listener) {
public final boolean removeChangeListener(DequeChangeListener<? super E> listener) {
return this.changeListeners.remove(Objects.requireNonNull(listener));
}

Expand Down Expand Up @@ -120,7 +114,7 @@ public final void addFirst(@Nullable E element) {
this.addFirstImpl(element);

try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logAdd(ObservableDeque.Site.HEAD, element);
changeBuilder.logAdd(DequeChangeListener.Site.HEAD, element);
}
}

Expand All @@ -131,7 +125,7 @@ public final void addLast(E element) {
this.addLastImpl(element);

try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logAdd(ObservableDeque.Site.TAIL, element);
changeBuilder.logAdd(DequeChangeListener.Site.TAIL, element);
}
}

Expand All @@ -141,7 +135,7 @@ public final void addLast(E element) {
public final boolean offerFirst(E element) {
if (this.offerFirstImpl(element)) {
try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logAdd(ObservableDeque.Site.HEAD, element);
changeBuilder.logAdd(DequeChangeListener.Site.HEAD, element);
}

return true;
Expand All @@ -156,7 +150,7 @@ public final boolean offerFirst(E element) {
public final boolean offerLast(@Nullable E element) {
if (this.offerLastImpl(element)) {
try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logAdd(ObservableDeque.Site.TAIL, element);
changeBuilder.logAdd(DequeChangeListener.Site.TAIL, element);
}

return true;
Expand All @@ -174,7 +168,7 @@ public final E removeFirst() {
E element = this.removeFirstImpl();

try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logRemove(ObservableDeque.Site.HEAD, element);
changeBuilder.logRemove(DequeChangeListener.Site.HEAD, element);
}

return element;
Expand All @@ -189,7 +183,7 @@ public final E removeLast() {
E element = this.removeLastImpl();

try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logRemove(ObservableDeque.Site.TAIL, element);
changeBuilder.logRemove(DequeChangeListener.Site.TAIL, element);
}

return element;
Expand All @@ -204,7 +198,7 @@ public final E pollFirst() {
E element = this.pollFirstImpl();

try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logRemove(ObservableDeque.Site.HEAD, element);
changeBuilder.logRemove(DequeChangeListener.Site.HEAD, element);
}

return element;
Expand All @@ -218,7 +212,7 @@ public final E pollLast() {
E element = this.pollLastImpl();

try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logRemove(ObservableDeque.Site.TAIL, element);
changeBuilder.logRemove(DequeChangeListener.Site.TAIL, element);
}

return element;
Expand All @@ -235,7 +229,7 @@ public final boolean removeFirstOccurrence(@Nullable Object object) {
itr.remove();

try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logRemove(ObservableDeque.Site.OPAQUE, element);
changeBuilder.logRemove(DequeChangeListener.Site.OPAQUE, element);
}

return true;
Expand All @@ -256,7 +250,7 @@ public final boolean removeLastOccurrence(@Nullable Object object) {
itr.remove();

try (ChangeBuilder changeBuilder = this.beginChange()) {
changeBuilder.logRemove(ObservableDeque.Site.OPAQUE, element);
changeBuilder.logRemove(DequeChangeListener.Site.OPAQUE, element);
}

return true;
Expand Down Expand Up @@ -334,7 +328,7 @@ public final E pop() {
*/
protected final class ChangeBuilder implements AutoCloseable {

private final List<ObservableDeque.LocalChange<E>> localChanges = new ArrayList<>(1);
private final List<DequeChangeListener.LocalChange<E>> localChanges = new ArrayList<>(1);

private int depth = 0;

Expand All @@ -358,15 +352,15 @@ public void close() {
AbstractObservableDeque.this.changeBuilder = null;
if (this.localChanges.isEmpty()) return;

var change = new ObservableDeque.Change<>(Collections.unmodifiableList(this.localChanges));
var change = new DequeChangeListener.Change<>(Collections.unmodifiableList(this.localChanges));

for (var listener : AbstractObservableDeque.this.changeListeners) {
if (listener.isInvalid()) {
AbstractObservableDeque.this.changeListeners.remove(listener);
continue;
}

listener.onChanged(change);
listener.onChanged(AbstractObservableDeque.this, change);
if (listener.isInvalid()) AbstractObservableDeque.this.changeListeners.remove(listener);
}

Expand All @@ -390,24 +384,24 @@ public void close() {
*
* @since 0.1.0
*/
public void logAdd(ObservableDeque.Site site, @Nullable E element) {
public void logAdd(DequeChangeListener.Site site, @Nullable E element) {
if (!this.localChanges.isEmpty()) {
int lastIndex = this.localChanges.size() - 1;
ObservableDeque.LocalChange<E> lastLocalChange = this.localChanges.get(lastIndex);
DequeChangeListener.LocalChange<E> lastLocalChange = this.localChanges.get(lastIndex);

if (lastLocalChange instanceof ObservableDeque.LocalChange.Insertion && site == lastLocalChange.getSite()) {
ArrayList<E> elements = new ArrayList<>(lastLocalChange.getElements());
if (lastLocalChange instanceof DequeChangeListener.LocalChange.Insertion && site == lastLocalChange.site()) {
ArrayList<E> elements = new ArrayList<>(lastLocalChange.elements());
elements.add(element);

this.localChanges.set(lastIndex, new ObservableDeque.LocalChange.Insertion<>(site, Collections.unmodifiableList(elements)));
this.localChanges.set(lastIndex, new DequeChangeListener.LocalChange.Insertion<>(site, Collections.unmodifiableList(elements)));
return;
}
}

ArrayList<E> elements = new ArrayList<>();
elements.add(element);

this.localChanges.add(new ObservableDeque.LocalChange.Insertion<>(site, Collections.unmodifiableList(elements)));
this.localChanges.add(new DequeChangeListener.LocalChange.Insertion<>(site, Collections.unmodifiableList(elements)));
}

/**
Expand All @@ -418,24 +412,24 @@ public void logAdd(ObservableDeque.Site site, @Nullable E element) {
*
* @since 0.1.0
*/
public void logRemove(ObservableDeque.Site site, @Nullable E element) {
public void logRemove(DequeChangeListener.Site site, @Nullable E element) {
if (!this.localChanges.isEmpty()) {
int lastIndex = this.localChanges.size() - 1;
ObservableDeque.LocalChange<E> lastLocalChange = this.localChanges.get(lastIndex);
DequeChangeListener.LocalChange<E> lastLocalChange = this.localChanges.get(lastIndex);

if (lastLocalChange instanceof ObservableDeque.LocalChange.Removal && site == lastLocalChange.getSite()) {
ArrayList<E> elements = new ArrayList<>(lastLocalChange.getElements());
if (lastLocalChange instanceof DequeChangeListener.LocalChange.Removal && site == lastLocalChange.site()) {
ArrayList<E> elements = new ArrayList<>(lastLocalChange.elements());
elements.add(element);

this.localChanges.set(lastIndex, new ObservableDeque.LocalChange.Removal<>(site, Collections.unmodifiableList(elements)));
this.localChanges.set(lastIndex, new DequeChangeListener.LocalChange.Removal<>(site, Collections.unmodifiableList(elements)));
return;
}
}

ArrayList<E> elements = new ArrayList<>();
elements.add(element);

this.localChanges.add(new ObservableDeque.LocalChange.Removal<>(site, Collections.unmodifiableList(elements)));
this.localChanges.add(new DequeChangeListener.LocalChange.Removal<>(site, Collections.unmodifiableList(elements)));
}

}
Expand Down
Loading

0 comments on commit b6b2c49

Please sign in to comment.