public void fire(
     DependentSubjectListener subject, Integer event, DependentSubjectListener listener) {
   // update the listener
   boolean incremented = listener.latestRevision < event.intValue();
   listener.latestRevision = event.intValue();
   // make sure the listener's dependencies were notified first
   listener.assertDependenciesSatisfiedRecursively(listener);
   // send the event forward
   if (incremented) {
     listener.increment(0);
   }
 }
 /**
  * Dependencies are satisfied if the latestRevision is at least the latestRevision of all
  * upstream {@link DependentSubjectListener}s.
  */
 public void assertDependenciesSatisfiedRecursively(DependentSubjectListener notified) {
   final Iterator<DependentSubjectListener> iter = upstreamSubjects.iterator();
   while (iter.hasNext()) {
     final DependentSubjectListener upstream = iter.next();
     upstream.assertDependenciesSatisfiedRecursively(notified);
     if (latestRevision < upstream.latestRevision)
       throw new IllegalStateException(
           "Dependencies not satisfied for "
               + notified
               + ", dependency "
               + this
               + " not updated by "
               + upstream
               + "!");
   }
 }
  /**
   * The publisher should throw an IllegalStateException when a cycle in the listener graph is
   * created.
   */
  public void testCycleThrows() {
    SequenceDependenciesEventPublisher publisher = new SequenceDependenciesEventPublisher();
    DependentSubjectListener a = new DependentSubjectListener("A", publisher);
    DependentSubjectListener b = new DependentSubjectListener("B", publisher);
    DependentSubjectListener c = new DependentSubjectListener("C", publisher);

    // simple cycle of three
    a.addListener(b);
    b.addListener(c);
    try {
      c.addListener(a);
      fail("Cycle not detected");
    } catch (IllegalStateException e) {
      // expected
    }

    // cycle of 10
    publisher = new SequenceDependenciesEventPublisher();
    DependentSubjectListener[] subjects = new DependentSubjectListener[10];
    for (int i = 0; i < 10; i++) {
      subjects[i] = new DependentSubjectListener("" + i, publisher);
      if (i > 0) subjects[i - 1].addListener(subjects[i]);
    }
    try {
      subjects[9].addListener(subjects[0]);
      fail("Cycle not detected");
    } catch (IllegalStateException e) {
      // expected
    }

    // cycle of 1
    publisher = new SequenceDependenciesEventPublisher();
    a = new DependentSubjectListener("A", publisher);
    try {
      a.addListener(a);
      fail("Cycle not detected");
    } catch (IllegalStateException e) {
      // expected
    }
  }
 /** Register the listener as dependent on the subject. */
 public void addListener(DependentSubjectListener listener) {
   downstreamListeners.add(listener);
   listener.upstreamSubjects.add(this);
   listener.latestRevision = Math.max(listener.latestRevision, latestRevision);
   this.publisher.addListener(this, listener, DependentSubjectListenerEventFormat.INSTANCE);
 }
  /**
   * Prepare the original diamond-dependency problem, where B depends on A and C depends on both A
   * and B. We need to guarantee that C can read all of its dependencies only when they're in a
   * 'consistent' state, which is when they've received all events from their dependencies, and
   * their dependencies are in a consistent state.
   *
   * <p>A --> B --. This diagram shows A --> B, B --> C and A --> C. | | The only safe notification
   * order of events is | V for B to receive events from A before C receives '-------> C those same
   * events.
   */
  public void testDiamondDependency() {

    SequenceDependenciesEventPublisher publisher = new SequenceDependenciesEventPublisher();
    DependentSubjectListener a = new DependentSubjectListener("A", publisher);
    DependentSubjectListener b = new DependentSubjectListener("B", publisher);
    DependentSubjectListener c = new DependentSubjectListener("C", publisher);

    a.addListener(c);
    a.increment(10);
    assertEquals(10, a.latestRevision);
    assertEquals(0, b.latestRevision);
    assertEquals(10, c.latestRevision);

    c.increment(5);
    assertEquals(10, a.latestRevision);
    assertEquals(0, b.latestRevision);
    assertEquals(15, c.latestRevision);

    b.increment(20);
    assertEquals(10, a.latestRevision);
    assertEquals(20, b.latestRevision);
    assertEquals(15, c.latestRevision);

    b.addListener(c);
    assertEquals(10, a.latestRevision);
    assertEquals(20, b.latestRevision);
    assertEquals(20, c.latestRevision);

    b.increment(2);
    assertEquals(10, a.latestRevision);
    assertEquals(22, b.latestRevision);
    assertEquals(22, c.latestRevision);

    a.increment(15);
    assertEquals(25, a.latestRevision);
    assertEquals(22, b.latestRevision);
    assertEquals(25, c.latestRevision);

    a.addListener(b);
    assertEquals(25, a.latestRevision);
    assertEquals(25, b.latestRevision);
    assertEquals(25, c.latestRevision);

    a.increment(4);
    assertEquals(29, a.latestRevision);
    assertEquals(29, b.latestRevision);
    assertEquals(29, c.latestRevision);
  }