/* ZZObs.java * * Copyright (c) 2000, Ted Nelson and Tuomas Lukka * * You may use and distribute under the terms of either the GNU Lesser * General Public License, either version 2 of the license or, * at your choice, any later version. Alternatively, you may use and * distribute under the terms of the XPL. * * See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of * the licenses. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the README * file for more details. * */ /* * Written by Tuomas Lukka, queuing and enable/disable by Rauli Ruohonen */ package org.gzigzag; import java.util.*; /** A class used to trigger ZZObses. */ public class ZZObsTrigger { String rcsid = "$Id: ZZObsTrigger.java,v 1.6.2.1 2001/04/08 16:13:59 ajk Exp $"; public static boolean dbg = false; private static void p(String s) { if(dbg) ZZLogger.log(s); } private static void pa(String s) { ZZLogger.log(s); } // XXX Store weak references, not hard ones! private static Vector allTrigs = new Vector(); private static Hashtable obsQueue = new Hashtable(); private static Hashtable obsDisabled = new Hashtable(); ZZObsTrigger() { allTrigs.addElement(this); } /** An object which keeps track of the observations of a single * ZZObs. */ class Gob { /** Linked list of Gobs, plus list of all objects that this * Gob observes. * This is a tricky part and this is also what makes us blazingly * fast. And difficult to explain. *

* First of all, there is a key for every object that this * gob's obs has been addObs'ed for. * The value depends on whether there are other gobs observing * the same thing. If there are, then the hashtables form a linked * list. If not, or if this is the last element of the linked * list, the value in the Hashtable is this gob. * That may seem odd until you realize that 1) hashtables can't * store nulls, and 2) this is a pointer that is * very likely to be in a register so fast to compare with * the reference. */ Hashtable next = new Hashtable(); /** The backward links. Only necessary links are stored. */ Hashtable prev = new Hashtable(); ZZObs obs; } /** Key: ZZObs; Value: Gob. */ Hashtable gobs = new Hashtable(); /** Key: Object (trigger); Value: Gob. */ Hashtable trigs = new Hashtable(); /** Called when something is changed. * This call removes instances of the ZZObses triggered from * all existing ZZObsTrigger objects. */ public synchronized void chg(Object o) { Gob g,pg=null; Hashtable next=trigs; while((g = (Gob)next.get(o)) !=null && g!=pg) { pg=g; if(!chg(g.obs)) next=g.next; } // XXX This shouldn't be necessary! (views should use triggers) ZZUpdateManager.chg(); } public static boolean chg(ZZObs obs) { // XXX This shouldn't be necessary! (views should use triggers) ZZUpdateManager.chg(); synchronized(obsQueue) { if(obsDisabled.containsKey(obs)) return false; obsQueue.put(obs,obs); for(int i=0; i only the * event thread may call general ZZ structure -using code. */ public static void runObsQueue() { p("runObsQueue enter"); synchronized(obsQueue) { while(obsQueue.size()>0) { p("STARTTRIGSEQ "+(obsQueue.size())); /* Trigger the queued observers in some order */ Hashtable q=obsQueue; obsQueue=new Hashtable(); for(Enumeration e=q.keys();e.hasMoreElements();) try { ((ZZObs)e.nextElement()).chg(); } catch(Exception ex) { ZZLogger.exc(ex); System.out.println("EXCEPTION WHILE TRIGGERING!"); } p("ENDTRIGSEQ "+(obsQueue.size())); } } p("runObsQueue leave"); } /** Enable/disable an observer. * If an observer is disabled, chg(o) won't trigger it. chg(o) calls * made before calling this method will be unaffected - that is, if * obs is in obsQueue, it will be triggered whether it is enabled or not. */ public static void setEnabled(ZZObs obs,boolean t) { if(t) obsDisabled.remove(obs); else obsDisabled.put(obs,obs); } }