001package arez.component;
002
003import arez.Arez;
004import arez.Disposable;
005import arez.SafeProcedure;
006import arez.annotations.CascadeDispose;
007import javax.annotation.Nonnull;
008import static org.realityforge.braincheck.Guards.*;
009
010/**
011 * Interface implemented by a component if it supports notifying listeners
012 * when the component is disposed. This notification occurs using a call-back
013 * and occurs within the dispose transaction (after {@link arez.annotations.PreDispose}
014 * is invoked if present) using a callback. Contrast this with the strategy used by
015 * {@link ComponentObservable} which uses standard Arez observables to track when
016 * a component is disposed.
017 */
018public interface DisposeNotifier
019{
020  /**
021   * Add the listener to notify list under key.
022   * This method MUST NOT be invoked after the component has been disposed.
023   * This method should not be invoked if another listener has been added with the same key without
024   * being removed.
025   *
026   * <p>If the key implements {@link Disposable} and {@link Disposable#isDisposed()} returns <code>true</code>
027   * when invoking the calback then the callback will be skipped. This rare situation only occurs when there is
028   * circular dependency in the object model usually involving {@link CascadeDispose}.</p>
029   *
030   * @param key    the key to uniquely identify listener.
031   * @param action the listener callback.
032   */
033  void addOnDisposeListener( @Nonnull Object key, @Nonnull SafeProcedure action );
034
035  /**
036   * Remove the listener with specified key from the notify list.
037   * This method should only be invoked when a listener has been added for specific key using
038   * {@link #addOnDisposeListener(Object, SafeProcedure)} and has not been removed by another
039   * call to this method.
040   *
041   * @param key the key under which the listener was previously added.
042   */
043  void removeOnDisposeListener( @Nonnull Object key );
044
045  /**
046   * Cast the specified object to an instance of DisposeNotifier.
047   * Invariant checks will verify that the cast is valid before proceeding.
048   *
049   * @param object the object.
050   * @return the object cast to DisposeNotifier.
051   */
052  @Nonnull
053  static DisposeNotifier asDisposeNotifier( @Nonnull final Object object )
054  {
055    if ( Arez.shouldCheckApiInvariants() )
056    {
057      apiInvariant( () -> object instanceof DisposeNotifier,
058                    () -> "Arez-0178: Object passed to asDisposeNotifier does not implement " +
059                          "DisposeNotifier. Object: " + object );
060    }
061    return (DisposeNotifier) object;
062  }
063}