001package arez;
002
003import arez.spy.ComputeCompleteEvent;
004import arez.spy.ComputeStartEvent;
005import arez.spy.ObserveCompleteEvent;
006import arez.spy.ObserveStartEvent;
007import arez.spy.ObserverCreateEvent;
008import arez.spy.ObserverDisposeEvent;
009import arez.spy.ObserverInfo;
010import grim.annotations.OmitSymbol;
011import java.util.ArrayList;
012import java.util.HashSet;
013import java.util.List;
014import java.util.Objects;
015import java.util.stream.Collectors;
016import javax.annotation.Nonnull;
017import javax.annotation.Nullable;
018import static org.realityforge.braincheck.Guards.*;
019
020/**
021 * A node within Arez that is notified of changes in 0 or more Observables.
022 */
023public final class Observer
024  extends Node
025{
026  /**
027   * The component that this observer is contained within.
028   * This should only be set if {@link Arez#areNativeComponentsEnabled()} is true but may also be null if
029   * the observer is a "top-level" observer.
030   */
031  @OmitSymbol( unless = "arez.enable_native_components" )
032  @Nullable
033  private final Component _component;
034  /**
035   * The reference to the ComputableValue if this observer is a derivation.
036   */
037  @Nullable
038  private final ComputableValue<?> _computableValue;
039  /**
040   * The observables that this observer receives notifications from.
041   * These are the dependencies within the dependency graph and will
042   * typically correspond to the observables that were accessed in last
043   * transaction that this observer was tracking.
044   *
045   * This list should contain no duplicates.
046   */
047  @Nonnull
048  private List<ObservableValue<?>> _dependencies = new ArrayList<>();
049  /**
050   * Observe function to invoke if any.
051   * This may be null if external executor is responsible for executing the observe function via
052   * methods such as {@link ArezContext#observe(Observer, Function, Object...)}. If this is null then
053   * {@link #_onDepsChange} must not be null.
054   */
055  @Nullable
056  private final Procedure _observe;
057  /**
058   * Callback invoked when dependencies are updated.
059   * This may be null when the observer re-executes the observe function when dependencies change
060   * but in that case {@link #_observe} must not be null.
061   */
062  @Nullable
063  private final Procedure _onDepsChange;
064  /**
065   * Cached info object associated with element.
066   * This should be null if {@link Arez#areSpiesEnabled()} is false.
067   */
068  @OmitSymbol( unless = "arez.enable_spies" )
069  @Nullable
070  private ObserverInfo _info;
071  /**
072   * A bitfield that contains config time and runtime flags/state.
073   * See the values in {@link Flags} that are covered by the masks
074   * {@link Flags#RUNTIME_FLAGS_MASK} and {@link Flags#CONFIG_FLAGS_MASK}
075   * for acceptable values.
076   */
077  private int _flags;
078  @Nonnull
079  private final Task _task;
080
081  Observer( @Nonnull final ComputableValue<?> computableValue, final int flags )
082  {
083    this( Arez.areZonesEnabled() ? computableValue.getContext() : null,
084          null,
085          Arez.areNamesEnabled() ? computableValue.getName() : null,
086          computableValue,
087          computableValue::compute,
088          null,
089          flags |
090          ( Flags.KEEPALIVE == Flags.getScheduleType( flags ) ? 0 : Flags.DEACTIVATE_ON_UNOBSERVE ) |
091          Task.Flags.runType( flags, Flags.KEEPALIVE == Flags.getScheduleType( flags ) ?
092                                     Task.Flags.RUN_NOW :
093                                     Task.Flags.RUN_LATER ) |
094          ( Arez.shouldEnforceTransactionType() ? Flags.READ_ONLY : 0 ) |
095          Flags.NESTED_ACTIONS_DISALLOWED |
096          Flags.dependencyType( flags ) );
097  }
098
099  Observer( @Nullable final ArezContext context,
100            @Nullable final Component component,
101            @Nullable final String name,
102            @Nullable final Procedure observe,
103            @Nullable final Procedure onDepsChange,
104            final int flags )
105  {
106    this( context,
107          component,
108          name,
109          null,
110          observe,
111          onDepsChange,
112          flags |
113          ( null == observe ? Flags.APPLICATION_EXECUTOR : Flags.KEEPALIVE ) |
114          Task.Flags.runType( flags, null == observe ? Task.Flags.RUN_LATER : Task.Flags.RUN_NOW ) |
115          Flags.nestedActionRule( flags ) |
116          Flags.dependencyType( flags ) |
117          Transaction.Flags.transactionMode( flags ) );
118  }
119
120  private Observer( @Nullable final ArezContext context,
121                    @Nullable final Component component,
122                    @Nullable final String name,
123                    @Nullable final ComputableValue<?> computableValue,
124                    @Nullable final Procedure observe,
125                    @Nullable final Procedure onDepsChange,
126                    final int flags )
127  {
128    super( context, name );
129    _task = new Task( context,
130                      name,
131                      this::invokeReaction,
132                      ( flags & Task.Flags.OBSERVER_TASK_FLAGS_MASK ) |
133                      Task.Flags.NO_REGISTER_TASK |
134                      Task.Flags.NO_WRAP_TASK );
135    _flags = ( flags & ~Task.Flags.OBSERVER_TASK_FLAGS_MASK ) | Flags.STATE_INACTIVE;
136    if ( Arez.shouldCheckInvariants() )
137    {
138      if ( Arez.shouldEnforceTransactionType() )
139      {
140        invariant( () -> Transaction.Flags.isTransactionModeValid( flags ),
141                   () -> "Arez-0079: Observer named '" + getName() + "' incorrectly specified both READ_ONLY " +
142                         "and READ_WRITE transaction mode flags." );
143      }
144      else
145      {
146        invariant( () -> !Transaction.Flags.isTransactionModeSpecified( flags ),
147                   () -> "Arez-0082: Observer named '" + getName() + "' specified transaction mode '" +
148                         Transaction.Flags.getTransactionModeName( flags ) + "' when " +
149                         "Arez.enforceTransactionType() is false." );
150      }
151      invariant( () -> Task.Flags.isPriorityValid( _task.getFlags() ),
152                 () -> "Arez-0080: Observer named '" + getName() + "' has invalid priority " +
153                       Task.Flags.getPriorityIndex( _task.getFlags() ) + "." );
154      invariant( () -> Task.Flags.isRunTypeValid( _task.getFlags() ),
155                 () -> "Arez-0081: Observer named '" + getName() + "' incorrectly specified both " +
156                       "RUN_NOW and RUN_LATER flags." );
157      invariant( () -> 0 == ( flags & Observer.Flags.RUN_NOW ) || null != observe,
158                 () -> "Arez-0206: Observer named '" + getName() + "' incorrectly specified " +
159                       "RUN_NOW flag but the observe function is null." );
160      invariant( () -> Arez.areNativeComponentsEnabled() || null == component,
161                 () -> "Arez-0083: Observer named '" + getName() + "' has component specified but " +
162                       "Arez.areNativeComponentsEnabled() is false." );
163      invariant( () -> Task.Flags.getPriority( flags ) != Task.Flags.PRIORITY_LOWEST ||
164                       0 == ( flags & Flags.OBSERVE_LOWER_PRIORITY_DEPENDENCIES ),
165                 () -> "Arez-0184: Observer named '" + getName() + "' has LOWEST priority but has passed " +
166                       "OBSERVE_LOWER_PRIORITY_DEPENDENCIES option which should not be present as the observer " +
167                       "has no lower priority." );
168      invariant( () -> null != observe || null != onDepsChange,
169                 () -> "Arez-0204: Observer named '" + getName() + "' has not supplied a value for either the " +
170                       "observe parameter or the onDepsChange parameter." );
171      // Next lines are impossible situations to create from tests. Add asserts to verify this.
172      assert Flags.KEEPALIVE != Flags.getScheduleType( flags ) || null != observe;
173      assert Flags.APPLICATION_EXECUTOR != Flags.getScheduleType( flags ) || null == observe;
174      assert !( Observer.Flags.RUN_NOW == ( flags & Observer.Flags.RUN_NOW ) &&
175                Flags.KEEPALIVE != Flags.getScheduleType( flags ) &&
176                null != computableValue );
177      invariant( () -> Flags.isNestedActionsModeValid( flags ),
178                 () -> "Arez-0209: Observer named '" + getName() + "' incorrectly specified both the " +
179                       "NESTED_ACTIONS_ALLOWED flag and the NESTED_ACTIONS_DISALLOWED flag." );
180      invariant( () -> Flags.isScheduleTypeValid( flags ),
181                 () -> "Arez-0210: Observer named '" + getName() + "' incorrectly specified multiple " +
182                       "schedule type flags (KEEPALIVE, DEACTIVATE_ON_UNOBSERVE, APPLICATION_EXECUTOR)." );
183      invariant( () -> ( ~( Flags.RUNTIME_FLAGS_MASK | Flags.CONFIG_FLAGS_MASK ) & flags ) == 0,
184                 () -> "Arez-0207: Observer named '" + getName() + "' specified illegal flags: " +
185                       ( ~( Flags.RUNTIME_FLAGS_MASK | Flags.CONFIG_FLAGS_MASK ) & flags ) );
186    }
187    assert null == computableValue || !Arez.areNamesEnabled() || computableValue.getName().equals( name );
188    _component = Arez.areNativeComponentsEnabled() ? component : null;
189    _computableValue = computableValue;
190    _observe = observe;
191    _onDepsChange = onDepsChange;
192
193    executeObserveNextIfPresent();
194
195    if ( null == _computableValue )
196    {
197      if ( null != _component )
198      {
199        _component.addObserver( this );
200      }
201      else if ( Arez.areRegistriesEnabled() )
202      {
203        getContext().registerObserver( this );
204      }
205    }
206    if ( null == _computableValue )
207    {
208      if ( willPropagateSpyEvents() )
209      {
210        getSpy().reportSpyEvent( new ObserverCreateEvent( asInfo() ) );
211      }
212      if ( null != _observe )
213      {
214        initialSchedule();
215      }
216    }
217  }
218
219  void initialSchedule()
220  {
221    getContext().scheduleReaction( this );
222    _task.triggerSchedulerInitiallyUnlessRunLater();
223  }
224
225  boolean areArezDependenciesRequired()
226  {
227    assert Arez.shouldCheckApiInvariants();
228    return Flags.AREZ_DEPENDENCIES == ( _flags & Flags.AREZ_DEPENDENCIES );
229  }
230
231  boolean areExternalDependenciesAllowed()
232  {
233    assert Arez.shouldCheckApiInvariants();
234    return Flags.AREZ_OR_EXTERNAL_DEPENDENCIES == ( _flags & Flags.AREZ_OR_EXTERNAL_DEPENDENCIES );
235  }
236
237  /**
238   * Return true if the Observer supports invocations of {@link #schedule()} from non-arez code.
239   * This is true if both a {@link #_observe} and {@link #_onDepsChange} parameters
240   * are provided at construction.
241   */
242  boolean supportsManualSchedule()
243  {
244    assert Arez.shouldCheckApiInvariants();
245    return null != _observe && null != _onDepsChange;
246  }
247
248  boolean isApplicationExecutor()
249  {
250    assert Arez.shouldCheckApiInvariants();
251    return null == _observe;
252  }
253
254  boolean nestedActionsAllowed()
255  {
256    assert Arez.shouldCheckApiInvariants();
257    return 0 != ( _flags & Flags.NESTED_ACTIONS_ALLOWED );
258  }
259
260  boolean canObserveLowerPriorityDependencies()
261  {
262    assert Arez.shouldCheckApiInvariants();
263    return 0 != ( _flags & Flags.OBSERVE_LOWER_PRIORITY_DEPENDENCIES );
264  }
265
266  boolean noReportResults()
267  {
268    assert Arez.areSpiesEnabled();
269    return 0 != ( _flags & Observer.Flags.NO_REPORT_RESULT );
270  }
271
272  boolean isComputableValue()
273  {
274    return null != _computableValue;
275  }
276
277  /**
278   * Make the Observer INACTIVE and release any resources associated with observer.
279   * The applications should NOT interact with the Observer after it has been disposed.
280   */
281  @Override
282  public void dispose()
283  {
284    if ( isNotDisposedOrDisposing() )
285    {
286      getContext().safeAction( Arez.areNamesEnabled() ? getName() + ".dispose" : null,
287                               this::performDispose,
288                               ActionFlags.NO_VERIFY_ACTION_REQUIRED );
289      if ( !isComputableValue() )
290      {
291        if ( willPropagateSpyEvents() )
292        {
293          reportSpyEvent( new ObserverDisposeEvent( asInfo() ) );
294        }
295        if ( null != _component )
296        {
297          _component.removeObserver( this );
298        }
299        else if ( Arez.areRegistriesEnabled() )
300        {
301          getContext().deregisterObserver( this );
302        }
303      }
304      if ( null != _computableValue )
305      {
306        _computableValue.dispose();
307      }
308      _task.dispose();
309      markAsDisposed();
310    }
311  }
312
313  private void performDispose()
314  {
315    getContext().getTransaction().reportDispose( this );
316    markDependenciesLeastStaleObserverAsUpToDate();
317    setState( Flags.STATE_DISPOSING );
318  }
319
320  void markAsDisposed()
321  {
322    _flags = Flags.setState( _flags, Flags.STATE_DISPOSED );
323  }
324
325  @Override
326  public boolean isDisposed()
327  {
328    return Flags.STATE_DISPOSED == getState();
329  }
330
331  boolean isNotDisposedOrDisposing()
332  {
333    return Flags.STATE_DISPOSING < getState();
334  }
335
336  /**
337   * Return true during invocation of dispose, false otherwise.
338   *
339   * @return true during invocation of dispose, false otherwise.
340   */
341  boolean isDisposing()
342  {
343    return Flags.STATE_DISPOSING == getState();
344  }
345
346  /**
347   * Return the state of the observer relative to the observers dependencies.
348   *
349   * @return the state of the observer relative to the observers dependencies.
350   */
351  int getState()
352  {
353    return Flags.getState( _flags );
354  }
355
356  int getLeastStaleObserverState()
357  {
358    return Flags.getLeastStaleObserverState( _flags );
359  }
360
361  /**
362   * Return true if observer creates a READ_WRITE transaction.
363   *
364   * @return true if observer creates a READ_WRITE transaction.
365   */
366  boolean isMutation()
367  {
368    assert Arez.shouldEnforceTransactionType();
369    return 0 != ( _flags & Flags.READ_WRITE );
370  }
371
372  /**
373   * Return true if the observer is active.
374   * Being "active" means that the state of the observer is not {@link Flags#STATE_INACTIVE},
375   * {@link Flags#STATE_DISPOSING} or {@link Flags#STATE_DISPOSED}.
376   *
377   * <p>An inactive observer has no dependencies and depending on the type of observer may
378   * have other consequences. i.e. An inactive observer will never be scheduled even if it has a
379   * reaction.</p>
380   *
381   * @return true if the Observer is active.
382   */
383  boolean isActive()
384  {
385    return Flags.isActive( _flags );
386  }
387
388  /**
389   * Return true if the observer is not active.
390   * The inverse of {@link #isActive()}
391   *
392   * @return true if the Observer is inactive.
393   */
394  boolean isInactive()
395  {
396    return !isActive();
397  }
398
399  /**
400   * This method should be invoked if the observer has non-arez dependencies and one of
401   * these dependencies has been updated. This will mark the observer as stale and reschedule
402   * the reaction if necessary. The method must be invoked from within a read-write transaction.
403   * the reaction if necessary. The method must be invoked from within a read-write transaction.
404   */
405  public void reportStale()
406  {
407    if ( Arez.shouldCheckApiInvariants() )
408    {
409      apiInvariant( this::areExternalDependenciesAllowed,
410                    () -> "Arez-0199: Observer.reportStale() invoked on observer named '" + getName() +
411                          "' but the observer has not specified AREZ_OR_EXTERNAL_DEPENDENCIES flag." );
412      apiInvariant( () -> getContext().isTransactionActive(),
413                    () -> "Arez-0200: Observer.reportStale() invoked on observer named '" + getName() +
414                          "' when there is no active transaction." );
415      apiInvariant( () -> getContext().getTransaction().isMutation(),
416                    () -> "Arez-0201: Observer.reportStale() invoked on observer named '" + getName() +
417                          "' when the active transaction '" + getContext().getTransaction().getName() +
418                          "' is READ_ONLY rather than READ_WRITE." );
419    }
420    if ( Arez.shouldEnforceTransactionType() && Arez.shouldCheckInvariants() )
421    {
422      getContext().getTransaction().markTransactionAsUsed();
423    }
424    setState( Flags.STATE_STALE );
425  }
426
427  /**
428   * Set the state of the observer.
429   * Call the hook actions for relevant state change.
430   * This is equivalent to passing true in <code>schedule</code> parameter to {@link #setState(int, boolean)}
431   *
432   * @param state the new state of the observer.
433   */
434  void setState( final int state )
435  {
436    setState( state, true );
437  }
438
439  /**
440   * Set the state of the observer.
441   * Call the hook actions for relevant state change.
442   *
443   * @param state    the new state of the observer.
444   * @param schedule true if a state transition can cause observer to reschedule, false otherwise.
445   */
446  void setState( final int state, final boolean schedule )
447  {
448    if ( Arez.shouldCheckInvariants() )
449    {
450      invariant( () -> getContext().isTransactionActive(),
451                 () -> "Arez-0086: Attempt to invoke setState on observer named '" + getName() + "' when there is " +
452                       "no active transaction." );
453      invariantState();
454    }
455    final int originalState = getState();
456    if ( state != originalState )
457    {
458      _flags = Flags.setState( _flags, state );
459      if ( Arez.shouldCheckInvariants() && Flags.STATE_DISPOSED == originalState )
460      {
461        fail( () -> "Arez-0087: Attempted to activate disposed observer named '" + getName() + "'." );
462      }
463      else if ( null == _computableValue && Flags.STATE_STALE == state )
464      {
465        if ( schedule )
466        {
467          scheduleReaction();
468        }
469      }
470      else if ( null != _computableValue &&
471                Flags.STATE_UP_TO_DATE == originalState &&
472                ( Flags.STATE_STALE == state || Flags.STATE_POSSIBLY_STALE == state ) )
473      {
474        _computableValue.getObservableValue().reportPossiblyChanged();
475        if ( schedule )
476        {
477          scheduleReaction();
478        }
479      }
480      else if ( Flags.STATE_INACTIVE == state ||
481                ( Flags.STATE_INACTIVE != originalState && Flags.STATE_DISPOSING == state ) )
482      {
483        if ( isComputableValue() )
484        {
485          final ComputableValue<?> computableValue = getComputableValue();
486          runHook( computableValue.getOnDeactivate(), ObserverError.ON_DEACTIVATE_ERROR );
487          computableValue.completeDeactivate();
488        }
489        clearDependencies();
490      }
491      else if ( Flags.STATE_INACTIVE == originalState &&
492                ( ( Flags.STATE_DISPOSED | Flags.STATE_DISPOSING ) & state ) == 0 )
493      {
494        if ( isComputableValue() )
495        {
496          runHook( getComputableValue().getOnActivate(), ObserverError.ON_ACTIVATE_ERROR );
497        }
498      }
499      if ( Arez.shouldCheckInvariants() )
500      {
501        invariantState();
502      }
503    }
504  }
505
506  /**
507   * Run the supplied hook if non null.
508   *
509   * @param hook the hook to run.
510   */
511  void runHook( @Nullable final Procedure hook, @Nonnull final ObserverError error )
512  {
513    if ( null != hook )
514    {
515      try
516      {
517        hook.call();
518      }
519      catch ( final Throwable t )
520      {
521        getContext().reportObserverError( this, error, t );
522      }
523    }
524  }
525
526  /**
527   * Remove all dependencies, removing this observer from all dependencies in the process.
528   */
529  void clearDependencies()
530  {
531    getDependencies().forEach( dependency -> {
532      dependency.removeObserver( this );
533      if ( !dependency.hasObservers() )
534      {
535        dependency.setLeastStaleObserverState( Flags.STATE_UP_TO_DATE );
536      }
537    } );
538    getDependencies().clear();
539  }
540
541  /**
542   * Return the task associated with the observer.
543   * The task is used during scheduling.
544   *
545   * @return the task associated with the observer.
546   */
547  @Nonnull
548  Task getTask()
549  {
550    return _task;
551  }
552
553  /**
554   * Schedule this observer if it does not already have a reaction pending.
555   * The observer will not actually react if it is not already marked as STALE.
556   */
557  public void schedule()
558  {
559    if ( Arez.shouldCheckApiInvariants() )
560    {
561      apiInvariant( this::supportsManualSchedule,
562                    () -> "Arez-0202: Observer.schedule() invoked on observer named '" + getName() +
563                          "' but supportsManualSchedule() returns false." );
564    }
565    if ( Arez.shouldEnforceTransactionType() && getContext().isTransactionActive() && Arez.shouldCheckInvariants() )
566    {
567      getContext().getTransaction().markTransactionAsUsed();
568    }
569    executeObserveNextIfPresent();
570    scheduleReaction();
571    getContext().triggerScheduler();
572  }
573
574  /**
575   * Schedule this observer if it does not already have a reaction pending.
576   */
577  void scheduleReaction()
578  {
579    if ( isNotDisposed() )
580    {
581      if ( Arez.shouldCheckInvariants() )
582      {
583        invariant( this::isActive,
584                   () -> "Arez-0088: Observer named '" + getName() + "' is not active but an attempt has been made " +
585                         "to schedule observer." );
586      }
587      if ( !getTask().isQueued() )
588      {
589        getContext().scheduleReaction( this );
590      }
591    }
592  }
593
594  /**
595   * Run the reaction in a transaction with the name and mode defined
596   * by the observer. If the reaction throws an exception, the exception is reported
597   * to the context global ObserverErrorHandlers
598   */
599  void invokeReaction()
600  {
601    if ( isNotDisposed() )
602    {
603      final long start;
604      if ( willPropagateSpyEvents() )
605      {
606        start = System.currentTimeMillis();
607        if ( isComputableValue() )
608        {
609          reportSpyEvent( new ComputeStartEvent( getComputableValue().asInfo() ) );
610        }
611        else
612        {
613          reportSpyEvent( new ObserveStartEvent( asInfo() ) );
614        }
615      }
616      else
617      {
618        start = 0;
619      }
620      Throwable error = null;
621      try
622      {
623        // ComputableValues may have calculated their values and thus be up to date so no need to recalculate.
624        if ( Flags.STATE_UP_TO_DATE != getState() )
625        {
626          if ( shouldExecuteObserveNext() )
627          {
628            executeOnDepsChangeNextIfPresent();
629            runObserveFunction();
630          }
631          else
632          {
633            assert null != _onDepsChange;
634            _onDepsChange.call();
635          }
636        }
637        else if ( shouldExecuteObserveNext() )
638        {
639          /*
640           * The observer should invoke onDepsChange next if the following conditions hold.
641           *  - a manual schedule() invocation
642           *  - the observer is not stale, and
643           *  - there is an onDepsChange hook present
644           *
645           *  This block ensures this is the case.
646           */
647          executeOnDepsChangeNextIfPresent();
648        }
649      }
650      catch ( final Throwable t )
651      {
652        error = t;
653        getContext().reportObserverError( this, ObserverError.REACTION_ERROR, t );
654      }
655      // start == 0 implies that spy events were enabled as part of observer, and thus we can skip this
656      // chain of events
657      if ( willPropagateSpyEvents() && 0 != start )
658      {
659        final long duration = System.currentTimeMillis() - start;
660        if ( isComputableValue() )
661        {
662          final ComputableValue<?> computableValue = getComputableValue();
663          reportSpyEvent( new ComputeCompleteEvent( computableValue.asInfo(),
664                                                    noReportResults() ? null : computableValue.getValue(),
665                                                    computableValue.getError(),
666                                                    (int) duration ) );
667        }
668        else
669        {
670          reportSpyEvent( new ObserveCompleteEvent( asInfo(), error, (int) duration ) );
671        }
672      }
673    }
674  }
675
676  private void runObserveFunction()
677    throws Throwable
678  {
679    assert null != _observe;
680    final Procedure action;
681    if ( Arez.shouldCheckInvariants() && areArezDependenciesRequired() )
682    {
683      action = () -> {
684        _observe.call();
685        final Transaction current = Transaction.current();
686
687        final List<ObservableValue<?>> observableValues = current.getObservableValues();
688        invariant( () -> Objects.requireNonNull( current.getTracker() ).isDisposing() ||
689                         ( null != observableValues && !observableValues.isEmpty() ),
690                   () -> "Arez-0172: Observer named '" + getName() + "' that does not use an external executor " +
691                         "completed observe function but is not observing any properties. As a result the observer " +
692                         "will never be rescheduled." );
693      };
694    }
695    else
696    {
697      action = _observe;
698    }
699    getContext().rawObserve( this, action, null );
700  }
701
702  /**
703   * Utility to mark all dependencies least stale observer as up to date.
704   * Used when the Observer is determined to be up todate.
705   */
706  void markDependenciesLeastStaleObserverAsUpToDate()
707  {
708    for ( final ObservableValue<?> dependency : getDependencies() )
709    {
710      dependency.setLeastStaleObserverState( Flags.STATE_UP_TO_DATE );
711    }
712  }
713
714  /**
715   * Determine if any dependency of the Observer has actually changed.
716   * If the state is POSSIBLY_STALE then recalculate any ComputableValue dependencies.
717   * If any ComputableValue dependencies actually changed then the STALE state will
718   * be propagated.
719   *
720   * <p>By iterating over the dependencies in the same order that they were reported and
721   * stopping on the first change, all the recalculations are only called for ComputableValues
722   * that will be tracked by derivation. That is because we assume that if the first N
723   * dependencies of the derivation doesn't change then the derivation should run the same way
724   * up until accessing N-th dependency.</p>
725   *
726   * @return true if the Observer should be recomputed.
727   */
728  boolean shouldCompute()
729  {
730    final int state = getState();
731    switch ( state )
732    {
733      case Flags.STATE_UP_TO_DATE:
734        return false;
735      case Flags.STATE_INACTIVE:
736      case Flags.STATE_STALE:
737        return true;
738      case Flags.STATE_POSSIBLY_STALE:
739      {
740        for ( final ObservableValue<?> observableValue : getDependencies() )
741        {
742          if ( observableValue.isComputableValue() )
743          {
744            final Observer owner = observableValue.getObserver();
745            final ComputableValue<?> computableValue = owner.getComputableValue();
746            try
747            {
748              computableValue.get();
749            }
750            catch ( final Throwable ignored )
751            {
752            }
753            // Call to get() will update this state if ComputableValue changed
754            if ( Flags.STATE_STALE == getState() )
755            {
756              return true;
757            }
758          }
759        }
760      }
761      break;
762      default:
763        if ( Arez.shouldCheckInvariants() )
764        {
765          fail( () -> "Arez-0205: Observer.shouldCompute() invoked on observer named '" + getName() +
766                      "' but observer is in state " + Flags.getStateName( getState() ) );
767        }
768    }
769    /*
770     * This results in POSSIBLY_STALE returning to UP_TO_DATE
771     */
772    markDependenciesLeastStaleObserverAsUpToDate();
773    return false;
774  }
775
776  /**
777   * Return the dependencies.
778   *
779   * @return the dependencies.
780   */
781  @Nonnull
782  List<ObservableValue<?>> getDependencies()
783  {
784    return _dependencies;
785  }
786
787  /**
788   * Replace the current set of dependencies with supplied dependencies.
789   * This should be the only mechanism via which the dependencies are updated.
790   *
791   * @param dependencies the new set of dependencies.
792   */
793  void replaceDependencies( @Nonnull final List<ObservableValue<?>> dependencies )
794  {
795    if ( Arez.shouldCheckInvariants() )
796    {
797      invariantDependenciesUnique( "Pre replaceDependencies" );
798    }
799    _dependencies = Objects.requireNonNull( dependencies );
800    if ( Arez.shouldCheckInvariants() )
801    {
802      invariantDependenciesUnique( "Post replaceDependencies" );
803      invariantDependenciesBackLink( "Post replaceDependencies" );
804      invariantDependenciesNotDisposed();
805    }
806  }
807
808  /**
809   * Ensure the dependencies list contain no duplicates.
810   * Should be optimized away if invariant checking is disabled.
811   *
812   * @param context some useful debugging context used in invariant checks.
813   */
814  void invariantDependenciesUnique( @Nonnull final String context )
815  {
816    if ( Arez.shouldCheckInvariants() )
817    {
818      invariant( () -> getDependencies().size() == new HashSet<>( getDependencies() ).size(),
819                 () -> "Arez-0089: " + context + ": The set of dependencies in observer named '" +
820                       getName() + "' is not unique. Current list: '" +
821                       getDependencies().stream().map( Node::getName ).collect( Collectors.toList() ) + "'." );
822    }
823  }
824
825  /**
826   * Ensure all dependencies contain this observer in the list of observers.
827   * Should be optimized away if invariant checking is disabled.
828   *
829   * @param context some useful debugging context used in invariant checks.
830   */
831  void invariantDependenciesBackLink( @Nonnull final String context )
832  {
833    if ( Arez.shouldCheckInvariants() )
834    {
835      getDependencies().forEach( observable ->
836                                   invariant( () -> observable.getObservers().contains( this ),
837                                              () -> "Arez-0090: " + context + ": Observer named '" + getName() +
838                                                    "' has ObservableValue dependency named '" + observable.getName() +
839                                                    "' which does not contain the observer in the list of " +
840                                                    "observers." ) );
841      invariantComputableValueObserverState();
842    }
843  }
844
845  /**
846   * Ensure all dependencies are not disposed.
847   */
848  void invariantDependenciesNotDisposed()
849  {
850    if ( Arez.shouldCheckInvariants() )
851    {
852      getDependencies().forEach( observable ->
853                                   invariant( observable::isNotDisposed,
854                                              () -> "Arez-0091: Observer named '" + getName() + "' has " +
855                                                    "ObservableValue dependency named '" + observable.getName() +
856                                                    "' which is disposed." ) );
857      invariantComputableValueObserverState();
858    }
859  }
860
861  /**
862   * Ensure that state field and other fields of the Observer are consistent.
863   */
864  void invariantState()
865  {
866    if ( Arez.shouldCheckInvariants() )
867    {
868      if ( isInactive() && !isDisposing() )
869      {
870        invariant( () -> getDependencies().isEmpty(),
871                   () -> "Arez-0092: Observer named '" + getName() + "' is inactive but still has dependencies: " +
872                         getDependencies().stream().map( Node::getName ).collect( Collectors.toList() ) + "." );
873      }
874      if ( null != _computableValue && _computableValue.isNotDisposed() )
875      {
876        final ObservableValue<?> observable = _computableValue.getObservableValue();
877        invariant( () -> Objects.equals( observable.isComputableValue() ? observable.getObserver() : null, this ),
878                   () -> "Arez-0093: Observer named '" + getName() + "' is associated with an ObservableValue that " +
879                         "does not link back to observer." );
880      }
881    }
882  }
883
884  void invariantComputableValueObserverState()
885  {
886    if ( Arez.shouldCheckInvariants() )
887    {
888      if ( isComputableValue() && isActive() && isNotDisposed() )
889      {
890        invariant( () -> !getComputableValue().getObservableValue().getObservers().isEmpty() ||
891                         Objects.equals( getContext().getTransaction().getTracker(), this ),
892                   () -> "Arez-0094: Observer named '" + getName() + "' is a ComputableValue and active but the " +
893                         "associated ObservableValue has no observers." );
894      }
895    }
896  }
897
898  /**
899   * Return the ComputableValue for Observer.
900   * This should not be called if observer is not part of a ComputableValue and will generate an invariant failure
901   * if invariants are enabled.
902   *
903   * @return the associated ComputableValue.
904   */
905  @Nonnull
906  ComputableValue<?> getComputableValue()
907  {
908    if ( Arez.shouldCheckInvariants() )
909    {
910      invariant( this::isComputableValue,
911                 () -> "Arez-0095: Attempted to invoke getComputableValue on observer named '" + getName() + "' when " +
912                       "is not a computable observer." );
913    }
914    assert null != _computableValue;
915    return _computableValue;
916  }
917
918  @Nullable
919  Component getComponent()
920  {
921    return _component;
922  }
923
924  /**
925   * Return the info associated with this class.
926   *
927   * @return the info associated with this class.
928   */
929  @SuppressWarnings( "ConstantConditions" )
930  @OmitSymbol( unless = "arez.enable_spies" )
931  @Nonnull
932  ObserverInfo asInfo()
933  {
934    if ( Arez.shouldCheckInvariants() )
935    {
936      invariant( Arez::areSpiesEnabled,
937                 () -> "Arez-0197: Observer.asInfo() invoked but Arez.areSpiesEnabled() returned false." );
938    }
939    if ( Arez.areSpiesEnabled() && null == _info )
940    {
941      _info = new ObserverInfoImpl( getContext().getSpy(), this );
942    }
943    return Arez.areSpiesEnabled() ? _info : null;
944  }
945
946  @Nullable
947  Procedure getObserve()
948  {
949    return _observe;
950  }
951
952  @Nullable
953  Procedure getOnDepsChange()
954  {
955    return _onDepsChange;
956  }
957
958  boolean isKeepAlive()
959  {
960    return Flags.KEEPALIVE == Flags.getScheduleType( _flags );
961  }
962
963  boolean shouldExecuteObserveNext()
964  {
965    return 0 != ( _flags & Flags.EXECUTE_OBSERVE_NEXT );
966  }
967
968  void executeObserveNextIfPresent()
969  {
970    if ( null != _observe )
971    {
972      _flags |= Flags.EXECUTE_OBSERVE_NEXT;
973    }
974  }
975
976  private void executeOnDepsChangeNextIfPresent()
977  {
978    if ( null != _onDepsChange )
979    {
980      _flags &= ~Flags.EXECUTE_OBSERVE_NEXT;
981    }
982  }
983
984  public static final class Flags
985  {
986    /**
987     * The flag can be passed to actions or observers to force the action to not report result to spy infrastructure.
988     */
989    public static final int NO_REPORT_RESULT = 1 << 12;
990    /**
991     * Highest priority.
992     * This priority should be used when the observer will dispose or release other reactive elements
993     * (and thus remove elements from being scheduled).
994     * <p>Only one of the PRIORITY_* flags should be applied to observer.</p>
995     *
996     * @see arez.annotations.Priority#HIGHEST
997     * @see arez.spy.Priority#HIGHEST
998     * @see Task.Flags#PRIORITY_HIGHEST
999     */
1000    public static final int PRIORITY_HIGHEST = 0b001 << 15;
1001    /**
1002     * High priority.
1003     * To reduce the chance that downstream elements will react multiple times within a single
1004     * reaction round, this priority should be used when the observer may trigger many downstream
1005     * reactions.
1006     * <p>Only one of the PRIORITY_* flags should be applied to observer.</p>
1007     *
1008     * @see arez.annotations.Priority#HIGH
1009     * @see arez.spy.Priority#HIGH
1010     * @see Task.Flags#PRIORITY_HIGH
1011     */
1012    public static final int PRIORITY_HIGH = 0b010 << 15;
1013    /**
1014     * Normal priority if no other priority otherwise specified.
1015     * <p>Only one of the PRIORITY_* flags should be applied to observer.</p>
1016     *
1017     * @see arez.annotations.Priority#NORMAL
1018     * @see arez.spy.Priority#NORMAL
1019     * @see Task.Flags#PRIORITY_NORMAL
1020     */
1021    public static final int PRIORITY_NORMAL = 0b011 << 15;
1022    /**
1023     * Low priority.
1024     * Usually used to schedule observers that reflect state onto non-reactive
1025     * application components. i.e. Observers that are used to build html views,
1026     * perform network operations etc. These reactions are often at low priority
1027     * to avoid recalculation of dependencies (i.e. {@link ComputableValue}s) triggering
1028     * this reaction multiple times within a single reaction round.
1029     * <p>Only one of the PRIORITY_* flags should be applied to observer.</p>
1030     *
1031     * @see arez.annotations.Priority#LOW
1032     * @see arez.spy.Priority#LOW
1033     * @see Task.Flags#PRIORITY_LOW
1034     */
1035    public static final int PRIORITY_LOW = 0b100 << 15;
1036    /**
1037     * Lowest priority. Use this priority if the observer is a {@link ComputableValue} that
1038     * may be unobserved when a {@link #PRIORITY_LOW} observer reacts. This is used to avoid
1039     * recomputing state that is likely to either be unobserved or recomputed as part of
1040     * another observers reaction.
1041     * <p>Only one of the PRIORITY_* flags should be applied to observer.</p>
1042     *
1043     * @see arez.annotations.Priority#LOWEST
1044     * @see arez.spy.Priority#LOWEST
1045     * @see Task.Flags#PRIORITY_LOWEST
1046     */
1047    public static final int PRIORITY_LOWEST = 0b101 << 15;
1048    /**
1049     * Mask used to extract priority bits.
1050     */
1051    public static final int PRIORITY_MASK = 0b111 << 15;
1052    /**
1053     * The observer can only read arez state.
1054     */
1055    public static final int READ_ONLY = 1 << 24;
1056    /**
1057     * The observer can read or write arez state.
1058     */
1059    public static final int READ_WRITE = 1 << 23;
1060    /**
1061     * The scheduler will be triggered when the observer is created to immediately invoke the
1062     * {@link Observer#_observe} function. This configuration should not be specified if there
1063     * is no {@link Observer#_observe} function supplied. This should not be
1064     * specified if {@link #RUN_LATER} is specified.
1065     */
1066    @SuppressWarnings( "WeakerAccess" )
1067    public static final int RUN_NOW = 1 << 22;
1068    /**
1069     * The scheduler will not be triggered when the observer is created. The observer either
1070     * has no {@link Observer#_observe} function or is responsible for ensuring that
1071     * {@link ArezContext#triggerScheduler()} is invoked at a later time. This should not be
1072     * specified if {@link #RUN_NOW} is specified.
1073     */
1074    public static final int RUN_LATER = 1 << 21;
1075    /**
1076     * Flag indicating that the Observer is allowed to observe {@link ComputableValue} instances with a lower priority.
1077     */
1078    public static final int OBSERVE_LOWER_PRIORITY_DEPENDENCIES = 1 << 30;
1079    /**
1080     * Indicates that the an action can be created from within the Observers observed function.
1081     */
1082    public static final int NESTED_ACTIONS_ALLOWED = 1 << 29;
1083    /**
1084     * Indicates that the an action must not be created from within the Observers observed function.
1085     */
1086    public static final int NESTED_ACTIONS_DISALLOWED = 1 << 28;
1087    /**
1088     * Flag set set if the application code can not invoke the {@link Observer#reportStale()} method.
1089     *
1090     * @see arez.annotations.DepType#AREZ
1091     */
1092    public static final int AREZ_DEPENDENCIES = 1 << 27;
1093    /**
1094     * Flag set set if the application code can not invokethe  {@link Observer#reportStale()} method to indicate
1095     * that a dependency has changed. In this scenario it is not an error if the observer does not invoke the
1096     * {@link ObservableValue#reportObserved()} on a dependency during it's reaction.
1097     *
1098     * @see arez.annotations.DepType#AREZ_OR_NONE
1099     */
1100    public static final int AREZ_OR_NO_DEPENDENCIES = 1 << 26;
1101    /**
1102     * Flag set if the application code can invoke the {@link Observer#reportStale()} method to indicate that a non-arez dependency has changed.
1103     *
1104     * @see arez.annotations.DepType#AREZ_OR_EXTERNAL
1105     */
1106    public static final int AREZ_OR_EXTERNAL_DEPENDENCIES = 1 << 25;
1107    /**
1108     * The runtime will keep the observer reacting to dependencies until disposed. This is the default value for
1109     * observers that supply a observed function.
1110     */
1111    public static final int KEEPALIVE = 1 << 20;
1112    /**
1113     * Mask used to extract dependency type bits.
1114     */
1115    private static final int DEPENDENCIES_TYPE_MASK =
1116      AREZ_DEPENDENCIES | AREZ_OR_NO_DEPENDENCIES | AREZ_OR_EXTERNAL_DEPENDENCIES;
1117    /**
1118     * Mask to extract "NESTED_ACTIONS" option so can derive default value if required.
1119     */
1120    private static final int NESTED_ACTIONS_MASK = NESTED_ACTIONS_ALLOWED | NESTED_ACTIONS_DISALLOWED;
1121    /**
1122     * Flag indicating whether next scheduled invocation of {@link Observer} should invoke {@link Observer#_observe} or {@link Observer#_onDepsChange}.
1123     */
1124    static final int EXECUTE_OBSERVE_NEXT = 1 << 9;
1125    /**
1126     * Mask used to extract state bits.
1127     * State is the lowest bits as it is the most frequently accessed numeric fields
1128     * and placing values at lower part of integer avoids a shift.
1129     */
1130    static final int STATE_MASK = 0b111;
1131    /**
1132     * Mask that identifies the bits associated with runtime configuration.
1133     */
1134    static final int RUNTIME_FLAGS_MASK = EXECUTE_OBSERVE_NEXT | STATE_MASK;
1135    /**
1136     * The observer has been disposed.
1137     */
1138    static final int STATE_DISPOSED = 0b001;
1139    /**
1140     * The observer is in the process of being disposed.
1141     */
1142    static final int STATE_DISPOSING = 0b010;
1143    /**
1144     * The observer is not active and is not holding any data about it's dependencies.
1145     * Typically mean this tracker observer has not been run or if it is a ComputableValue that
1146     * there is no observer observing the associated ObservableValue.
1147     */
1148    static final int STATE_INACTIVE = 0b011;
1149    /**
1150     * No change since last time observer was notified.
1151     */
1152    static final int STATE_UP_TO_DATE = 0b100;
1153    /**
1154     * A transitive dependency has changed but it has not been determined if a shallow
1155     * dependency has changed. The observer will need to check if shallow dependencies
1156     * have changed. Only Derived observables will propagate POSSIBLY_STALE state.
1157     */
1158    static final int STATE_POSSIBLY_STALE = 0b101;
1159    /**
1160     * A dependency has changed so the observer will need to recompute.
1161     */
1162    static final int STATE_STALE = 0b110;
1163    /**
1164     * The flag is valid on observers associated with computable values and will deactivate the observer if the
1165     * computable value has no observers.
1166     */
1167    static final int DEACTIVATE_ON_UNOBSERVE = 1 << 19;
1168    /**
1169     * The flag is valid on observers where the observed function is invoked by the application.
1170     */
1171    static final int APPLICATION_EXECUTOR = 1 << 18;
1172    /**
1173     * Mask used to extract react type bits.
1174     */
1175    static final int SCHEDULE_TYPE_MASK = KEEPALIVE | DEACTIVATE_ON_UNOBSERVE | APPLICATION_EXECUTOR;
1176    /**
1177     * Mask that identifies the bits associated with static configuration.
1178     */
1179    static final int CONFIG_FLAGS_MASK =
1180      PRIORITY_MASK |
1181      RUN_NOW | RUN_LATER |
1182      READ_ONLY | READ_WRITE |
1183      OBSERVE_LOWER_PRIORITY_DEPENDENCIES |
1184      NESTED_ACTIONS_MASK |
1185      DEPENDENCIES_TYPE_MASK |
1186      SCHEDULE_TYPE_MASK |
1187      NO_REPORT_RESULT;
1188
1189    /**
1190     * Extract and return the observer's state.
1191     *
1192     * @param flags the flags.
1193     * @return the state.
1194     */
1195    static int getState( final int flags )
1196    {
1197      return flags & STATE_MASK;
1198    }
1199
1200    /**
1201     * Return the new value of flags when supplied with specified state.
1202     *
1203     * @param flags the flags.
1204     * @param state the new state.
1205     * @return the new flags.
1206     */
1207    static int setState( final int flags, final int state )
1208    {
1209      return ( flags & ~STATE_MASK ) | state;
1210    }
1211
1212    /**
1213     * Return true if the state is UP_TO_DATE, POSSIBLY_STALE or STALE.
1214     * The inverse of {@link #isNotActive(int)}
1215     *
1216     * @param flags the flags to check.
1217     * @return true if the state is UP_TO_DATE, POSSIBLY_STALE or STALE.
1218     */
1219    static boolean isActive( final int flags )
1220    {
1221      return getState( flags ) > STATE_INACTIVE;
1222    }
1223
1224    /**
1225     * Return true if the state is INACTIVE, DISPOSING or DISPOSED.
1226     * The inverse of {@link #isActive(int)}
1227     *
1228     * @param flags the flags to check.
1229     * @return true if the state is INACTIVE, DISPOSING or DISPOSED.
1230     */
1231    static boolean isNotActive( final int flags )
1232    {
1233      return !isActive( flags );
1234    }
1235
1236    /**
1237     * Return the least stale observer state. if the state is not active
1238     * then the {@link #STATE_UP_TO_DATE} will be returned.
1239     *
1240     * @param flags the flags to check.
1241     * @return the least stale observer state.
1242     */
1243    static int getLeastStaleObserverState( final int flags )
1244    {
1245      final int state = getState( flags );
1246      return state > STATE_INACTIVE ? state : STATE_UP_TO_DATE;
1247    }
1248
1249    /**
1250     * Return the state as a string.
1251     *
1252     * @param state the state value. One of the STATE_* constants
1253     * @return the string describing state.
1254     */
1255    @Nonnull
1256    static String getStateName( final int state )
1257    {
1258      assert Arez.shouldCheckInvariants() || Arez.shouldCheckApiInvariants();
1259      switch ( state )
1260      {
1261        case STATE_DISPOSED:
1262          return "DISPOSED";
1263        case STATE_DISPOSING:
1264          return "DISPOSING";
1265        case STATE_INACTIVE:
1266          return "INACTIVE";
1267        case STATE_POSSIBLY_STALE:
1268          return "POSSIBLY_STALE";
1269        case STATE_STALE:
1270          return "STALE";
1271        case STATE_UP_TO_DATE:
1272          return "UP_TO_DATE";
1273        default:
1274          return "UNKNOWN(" + state + ")";
1275      }
1276    }
1277
1278    static int nestedActionRule( final int flags )
1279    {
1280      return Arez.shouldCheckInvariants() ?
1281             0 != ( flags & NESTED_ACTIONS_MASK ) ? 0 : NESTED_ACTIONS_DISALLOWED :
1282             0;
1283    }
1284
1285    /**
1286     * Return true if flags contains a valid nested action mode.
1287     *
1288     * @param flags the flags.
1289     * @return true if flags contains valid nested action mode.
1290     */
1291    static boolean isNestedActionsModeValid( final int flags )
1292    {
1293      return NESTED_ACTIONS_ALLOWED == ( flags & NESTED_ACTIONS_ALLOWED ) ^
1294             NESTED_ACTIONS_DISALLOWED == ( flags & NESTED_ACTIONS_DISALLOWED );
1295    }
1296
1297    /**
1298     * Return the default dependency type flag if dependency type not specified.
1299     *
1300     * @param flags the flags.
1301     * @return the default dependency type if dependency type unspecified else 0.
1302     */
1303    static int dependencyType( final int flags )
1304    {
1305      return Arez.shouldCheckInvariants() ? 0 != ( flags & DEPENDENCIES_TYPE_MASK ) ? 0 : AREZ_DEPENDENCIES : 0;
1306    }
1307
1308    /**
1309     * Extract and return the schedule type.
1310     *
1311     * @param flags the flags.
1312     * @return the schedule type.
1313     */
1314    static int getScheduleType( final int flags )
1315    {
1316      return flags & SCHEDULE_TYPE_MASK;
1317    }
1318
1319    /**
1320     * Return true if flags contains a valid ScheduleType.
1321     *
1322     * @param flags the flags.
1323     * @return true if flags contains a valid ScheduleType.
1324     */
1325    static boolean isScheduleTypeValid( final int flags )
1326    {
1327      return KEEPALIVE == ( flags & KEEPALIVE ) ^
1328             DEACTIVATE_ON_UNOBSERVE == ( flags & DEACTIVATE_ON_UNOBSERVE ) ^
1329             APPLICATION_EXECUTOR == ( flags & APPLICATION_EXECUTOR );
1330    }
1331  }
1332}