| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- package org.leumasjaffe.observer;
- import java.lang.ref.WeakReference;
- import java.util.UUID;
- import com.google.common.collect.LinkedListMultimap;
- import com.google.common.collect.Multimap;
- import lombok.AccessLevel;
- import lombok.AllArgsConstructor;
- import lombok.experimental.FieldDefaults;
- import lombok.experimental.UtilityClass;
- /**
- * The master object that associates observale objects with listeners and invocations
- * TODO: May crash if updating an object where a Listener was destroyed without unsub
- * TODO: Potentially leaks memory if Listeners go out of scope without unsub
- * TODO: No way to drop Observable objects as they go out of scope
- */
- @UtilityClass
- @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
- public class ObserverDispatch {
- @AllArgsConstructor
- @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
- private static class Pair {
- WeakReference<Object> obj;
- Subscriber sub;
- }
-
- Multimap<UUID, Pair> observers = LinkedListMultimap.create();
-
- /**
- * Register a listener callback
- * @param target The object we're observing. Make sure that it's an observable and not just
- * some random UUID someone came up with to harass me.
- * @param src The Listener object that we are using, used for invalidation in case we
- * forget to invoke {@see unsubscribeAll} when destroying an object.
- * @param sub A callback function with a signature of void() that will be triggered when
- * we request an update.
- */
- public void subscribe(Observable target, Object src, Subscriber sub) {
- observers.put(target.observableId(), new Pair(new WeakReference<>(src), sub));
- }
-
- /**
- * Remove every callback that this listener is associated with
- * @param src A Listener object that was generally provided in a previous {@see subscribe}
- * call.
- */
- public void unsubscribeAll(Object src) {
- if (src == null) return;
- observers.entries().removeIf( e -> e.getValue().obj.get() == src );
- }
-
- /**
- * Dispatch notices of an update to an observable object that comes from an owning
- * {@see IndirectObservableListener}.
- * @param target The Observable that was updated by our caller
- * @param src An owning Listener that does not need to be updated.
- */
- void notifySubscribers(Observable target, IndirectObservableListener<?, ?> src) {
- observers.get(target.observableId()).stream().filter(
- p -> p.obj.get() != src).forEach( p -> p.sub.notifyUpdate() );
- }
- /**
- * Dispatch notices of an update to an observable object.
- * @param target The Observable that was updated by our caller
- */
- public void notifySubscribers(Observable target) {
- observers.get(target.observableId()).stream().forEach( p -> p.sub.notifyUpdate() );
- }
- }
|