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 the 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 * @param errorIfDuplicate generate an assertion error if duplicate key inserted. 033 */ 034 void addOnDisposeListener( @Nonnull Object key, @Nonnull SafeProcedure action, boolean errorIfDuplicate ); 035 036 /** 037 * Add the listener to the notify list under key. 038 * This method MUST NOT be invoked after the component has been disposed. 039 * This method should not be invoked if another listener has been added with the same key without 040 * being removed. 041 * 042 * <p>If the key implements {@link Disposable} and {@link Disposable#isDisposed()} returns <code>true</code> 043 * when invoking the calback then the callback will be skipped. This rare situation only occurs when there is 044 * circular dependency in the object model usually involving {@link CascadeDispose}.</p> 045 * 046 * @param key the key to uniquely identify listener. 047 * @param action the listener callback. 048 */ 049 default void addOnDisposeListener( @Nonnull final Object key, @Nonnull final SafeProcedure action ) 050 { 051 addOnDisposeListener( key, action, true ); 052 } 053 054 /** 055 * Remove the listener with the specified key from the notify list. 056 * This method should only be invoked when a listener has been added for specific key using 057 * {@link #addOnDisposeListener(Object, SafeProcedure, boolean)} and has not been removed by another 058 * call to this method. 059 * 060 * @param key the key under which the listener was previously added. 061 * @param errorIfMissing generate an assertion error if no such key exists. 062 */ 063 void removeOnDisposeListener( @Nonnull Object key, boolean errorIfMissing ); 064 065 /** 066 * Remove the listener with the specified key from the notify list. 067 * This method should only be invoked when a listener has been added for specific key using 068 * {@link #addOnDisposeListener(Object, SafeProcedure, boolean)} and has not been removed by another 069 * call to this method. 070 * 071 * @param key the key under which the listener was previously added. 072 */ 073 default void removeOnDisposeListener( @Nonnull final Object key ) 074 { 075 removeOnDisposeListener( key, true ); 076 } 077 078 /** 079 * Cast the specified object to an instance of DisposeNotifier. 080 * Invariant checks will verify that the cast is valid before proceeding. 081 * 082 * @param object the object. 083 * @return the object cast to DisposeNotifier. 084 */ 085 @Nonnull 086 static DisposeNotifier asDisposeNotifier( @Nonnull final Object object ) 087 { 088 if ( Arez.shouldCheckApiInvariants() ) 089 { 090 apiInvariant( () -> object instanceof DisposeNotifier, 091 () -> "Arez-0178: Object passed to asDisposeNotifier does not implement " + 092 "DisposeNotifier. Object: " + object ); 093 } 094 return (DisposeNotifier) object; 095 } 096}