code: /**
   *   Copyright (c) Rich Hickey. All rights reserved.
   *   The use and distribution terms for this software are covered by the
   *   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
   *   which can be found in the file epl-v10.html at the root of this distribution.
   *   By using this software in any fashion, you are agreeing to be bound by
   * 	 the terms of this license.
   *   You must not remove this notice, or any other, from this software.
   **/
  
  /* rich Jan 1, 2009 */
  
  package clojure.lang;
  
  import java.util.Map;
  
  public abstract class ARef extends AReference implements IRef{
  protected volatile IFn validator = null;
  private volatile IPersistentMap watches = PersistentHashMap.EMPTY;
  
  public ARef(){
  	super();
  }
  
  public ARef(IPersistentMap meta){
  	super(meta);
  }
  
  void validate(IFn vf, Object val){
  	try
  		{
  		if(vf != null && !RT.booleanCast(vf.invoke(val)))
  			throw new IllegalStateException("Invalid reference state");
  		}
  	catch(RuntimeException re)
  		{
  		throw re;
  		}
  	catch(Exception e)
  		{
  		throw new IllegalStateException("Invalid reference state", e);
  		}
  }
  
  void validate(Object val){
  	validate(validator, val);
  }
  
  public void setValidator(IFn vf){
  	validate(vf, deref());
  	validator = vf;
  }
  
  public IFn getValidator(){
  	return validator;
  }
  
  public IPersistentMap getWatches(){
  	return watches;
  }
  
  synchronized public IRef addWatch(Object key, IFn callback){
  	watches = watches.assoc(key, callback);
  	return this;
  }
  
  synchronized public IRef removeWatch(Object key){
  	watches = watches.without(key);
  	return this;
  }
  
  public void notifyWatches(Object oldval, Object newval){
  	IPersistentMap ws = watches;
  	if(ws.count() > 0)
  		{
  		for(ISeq s = ws.seq(); s != null; s = s.next())
  			{
  			Map.Entry e = (Map.Entry) s.first();
  			IFn fn = (IFn) e.getValue();
  			if(fn != null)
                                  fn.invoke(e.getKey(), this, oldval, newval);
  			}
  		}
  }
  }
  