/10/ design patterns: Observer implementation (practice)
// PACKAGE
AsSetupPackage("org.moock.util");

// CLASS Observable

/**
 * A Java-style Observable class used to represent the "subject"
 * of the Observer design pattern. Views must implement the Observer
 * interface, and register to observe the model via addObserver().
 */
org.moock.util.Observable = function () {
  this.observers = new Array();
}

// PROPERTIES
// A flag indicating whether this object has changed.
org.moock.util.Observable.prototype.changed = false;

// A list of observers.
org.moock.util.Observable.prototype.observers = null;



// METHODS
  /**
   * Adds an observer for this object.
   * @param   o  The observer to be added.
   */
  org.moock.util.Observable.prototype.addObserver = function (o) {
    // Can't add a null observer.
    if (o == null) {
      return false;
    }

    // Don't add an observer more than once.
    for (var i = 0; i < this.observers.length; i++) {
      if (this.observers[i] == o) {
        // The observer is already observing, so quit.
        return false;
      }
    }

    // Put the observer into the list.
    this.observers.push(o);
    return true;
  }

  /**
   * Removes an observer from this object.
   *
   * @param   o The observer to remove.
   */
  org.moock.util.Observable.prototype.deleteObserver = function (o) {
    // Find and remove the observer.
    var len = this.observers.length;
    for (var i = 0; i < len; i++) {
      if (this.observers[i] == o) {
        this.observers.splice(i, 1);
        return true;
      }
    }
    return false;
  }

  /**
   * Tell all observers that this object has changed.
   *
   * @param   infoObj   An object containing arbitrary data 
   *                    to pass to the observer.
   */
  org.moock.util.Observable.prototype.notifyObservers = function (infoObj) {
    // Use a null infoObject if none is supplied.
    if (infoObj == undefined) {
      infoObj = null;
    }

    // If the object hasn't changed, don't bother notifying observers.
    if (!this.changed) {
      return;
    }

    // Make a copy of the observers array. We do this
    // so that we can be sure the list won't change while
    // we're processing it.
    var observersSnapshot = this.observers.slice(0);
    // This change has been processed, unset the "changed" flag.
    this.clearChanged();

    // Invoke update() on all observers.
    trace("Notifying observers: " + this.observers);
    for (var i = observersSnapshot.length-1; i >= 0; i--) {
      observersSnapshot[i].update(this, infoObj);
    }
  }

  /**
   * Removes all observers from this object.
   */
  org.moock.util.Observable.prototype.deleteObservers = function () {
    this.observers = new Array();
  }

  /**
   * Indicates that this object has changed.
   */
  org.moock.util.Observable.prototype.setChanged = function () {
    this.changed = true;
  }

  /**
   * Indicates that this object has either not changed or
   * has notified its observers of the most recent change.
   */
  org.moock.util.Observable.prototype.clearChanged = function () {
    this.changed = false;
  }

  /**
   * Checks if the object has changed.
   *
   * @return   true if the object has changed, as determined by setChanged().
   */
  org.moock.util.Observable.prototype.hasChanged = function () {
    return this.changed;
  }

  /**
   * Returns the number of observers for this object.
   *
   * @return   An integer: the number of observers for this object.
   */
  org.moock.util.Observable.prototype.countObservers = function () {
    return this.observers.length;
  }

/**
 * interface org.moock.util.Observer
 * 
 * This interface must be implemented by all observers of an
 * Observable object.
 */

 /**
  * Observer.update(o, infoObj)
  *
  * Invoked automatically by an observed object when it changes.
  * 
  * @param   o   The observed object (an instance of Observable).
  * @param   infoObj   An arbitrary data object sent by 
  *                    the observed object.
  */