//: c10:ObservedFlower.java
// Demonstration of "observer" pattern.
import java.util.*;
import com.bruceeckel.test.*;

class Flower {
  private boolean isOpen;
  private OpenNotifier oNotify = 
    new OpenNotifier();
  private CloseNotifier cNotify = 
    new CloseNotifier();
  public Flower() { isOpen = false; }
  public void open() { // Opens its petals
    isOpen = true;
    oNotify.notifyObservers();
    cNotify.open();
  }
  public void close() { // Closes its petals
    isOpen = false;
    cNotify.notifyObservers();
    oNotify.close();
  }
  public Observable opening() { return oNotify; }
  public Observable closing() { return cNotify; }
  private class OpenNotifier extends Observable {
    private boolean alreadyOpen = false;
    public void notifyObservers() {
      if(isOpen && !alreadyOpen) {
        setChanged();
        super.notifyObservers();
        alreadyOpen = true;
      }
    }
    public void close() { alreadyOpen = false; }
  }
  private class CloseNotifier extends Observable{
    private boolean alreadyClosed = false;
    public void notifyObservers() {
      if(!isOpen && !alreadyClosed) {
        setChanged();
        super.notifyObservers();
        alreadyClosed = true;
      }
    }
    public void open() { alreadyClosed = false; }
  }
}

class Bee {
  private String name;
  private OpenObserver openObsrv = 
    new OpenObserver();
  private CloseObserver closeObsrv = 
    new CloseObserver();
  public Bee(String nm)  { name = nm; }
  // An inner class for observing openings:
  private class OpenObserver implements Observer{
    public void update(Observable ob, Object a) {
      System.out.println("Bee " + name 
        + "'s breakfast time!");
    }
  }
  // Another inner class for closings:
  private class CloseObserver implements Observer{
    public void update(Observable ob, Object a) {
      System.out.println("Bee " + name 
        + "'s bed time!");
    }
  }
  public Observer openObserver() { 
    return openObsrv; 
  }
  public Observer closeObserver() { 
    return closeObsrv;
  }
}

class Hummingbird {
  private String name;
  private OpenObserver openObsrv = 
    new OpenObserver();
  private CloseObserver closeObsrv = 
    new CloseObserver();
  public Hummingbird(String nm) { name = nm; }
  private class OpenObserver implements Observer{
    public void update(Observable ob, Object a) {
      System.out.println("Hummingbird " + name 
        + "'s breakfast time!");
    }
  }
  private class CloseObserver implements Observer{
    public void update(Observable ob, Object a) {
      System.out.println("Hummingbird " + name 
        + "'s bed time!");
    }
  }
  public Observer openObserver() { 
    return openObsrv; 
  }
  public Observer closeObserver() { 
    return closeObsrv;
  }
}

public class ObservedFlower extends UnitTest {
  Flower f = new Flower();
  Bee 
    ba = new Bee("A"), 
    bb = new Bee("B");
  Hummingbird 
    ha = new Hummingbird("A"), 
    hb = new Hummingbird("B");
  public void test() {
    f.opening().addObserver(ha.openObserver());
    f.opening().addObserver(hb.openObserver());
    f.opening().addObserver(ba.openObserver());
    f.opening().addObserver(bb.openObserver());
    f.closing().addObserver(ha.closeObserver());
    f.closing().addObserver(hb.closeObserver());
    f.closing().addObserver(ba.closeObserver());
    f.closing().addObserver(bb.closeObserver());
    // Hummingbird B decides to sleep in:
    f.opening().deleteObserver(
      hb.openObserver());
    // A change that interests observers:
    f.open();
    f.open(); // It's already open, no change.
    // Bee A doesn't want to go to bed:
    f.closing().deleteObserver(
      ba.closeObserver());
    f.close();
    f.close(); // It's already closed; no change
    f.opening().deleteObservers();
    f.open();
    f.close();
  }
  public static void main(String args[]) {
    new ObservedFlower().test();
  }
} ///:~
