/* * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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 GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package org.visage.runtime; import java.util.ArrayList; import java.util.List; /** * Manages dependents of a particular VisageObject. * * @author A. Sundararajan */ public final class DependentsManager { public static void addDependent(VisageObject bindee, final int varNum, VisageObject binder, final int depNum) { Dep dep = Dep.newDependency(binder, depNum); dep.linkToBindee(bindee, varNum); // FIXME: revisit this - is this a good time to call cleanup? WeakBinderRef.checkForCleanups(); } public static void removeDependent(VisageObject bindee, final int varNum, VisageObject binder) { // We need to find the "intersection Dep" between the Dep chain // associated with binder, and that associated with (bindee,varNum). // (Note that this manager is associated with bindee.) // Since the Dep chain assocated with the binder is limited by the // complexity of the bind expression, that chain is likely to be short, // so a linear search is OK. // We still search for the correct DepChain, so we know when to stop. // However, that is logarithmic on the maximum varNum, so should also // be fast. An alternative is to store (bindee,varNum) in each Dep, // which would cost one more field per Dep (compared to storing // the DepChain). DepChain chain = DepChain.find(varNum, bindee.getDepChain$internal$()); if (chain == null) return; Dep prev = null; WeakBinderRef binderRef = WeakBinderRef.instance(binder); for (Dep dep = binderRef.bindees; dep != null; ) { Dep next = dep.nextInBindees; if (dep.chain == chain) { if (prev == null) binderRef.bindees = next; else prev.nextInBindees = next; dep.unlinkFromBindee(); return; } prev = dep; dep = next; } } public static void switchDependence(VisageObject binder, VisageObject oldBindee, final int oldNum, VisageObject newBindee, final int newNum, final int depNum) { if (oldBindee != null) { oldBindee.removeDependent$(oldNum, binder); } if (newBindee != null) { newBindee.addDependent$(newNum, binder, depNum); } } public static void notifyDependents(VisageObject bindee, final int varNum, int startPos, int endPos, int newLength, final int phase) { DepChain chain = DepChain.find(varNum, bindee.getDepChain$internal$()); if (chain == null) return; for (Dep dep = chain.dependencies; dep != null;) { Dep next = dep.nextInBinders; WeakBinderRef binderRef = dep.binderRef; // Note that unlinkFromBindee might have been called earlier // on Dep, in which case binderRef will be null. In that case, // we don't need to do anything, except move on to the next Dep. if (binderRef != null) { VisageObject binder = binderRef.get(); if (binder == null) { binderRef.cleanup(); } else { boolean handled = true; try { handled = binder.update$(bindee, dep.depNum, startPos, endPos, newLength, phase); } catch (RuntimeException re) { ErrorHandler.bindException(re); } if (!handled) { Dep prev = null; for (Dep d = binderRef.bindees; d != null; ) { Dep nextInBindees = d.nextInBindees; if (d == dep) { if (prev == null) binderRef.bindees = nextInBindees; else prev.nextInBindees = nextInBindees; dep.unlinkFromBindee(); break; } prev = d; d = nextInBindees; } } } } dep = next; } } public static int getListenerCount(VisageObject bindee) { return getListenerCount(bindee.getDepChain$internal$()); } private static int getListenerCount(DepChain chain) { if (chain == null) return 0; int count = 0; for (Dep dep = chain.dependencies; dep != null;) { WeakBinderRef binderRef = dep.binderRef; if (binderRef != null) { if (binderRef.get() != null) { count++; } } dep = dep.nextInBinders; } return count + getListenerCount(chain.child0) + getListenerCount(chain.child1); } public static List<VisageObject> getDependents(VisageObject bindee) { List<VisageObject> res = new ArrayList<VisageObject>(); getDependents(bindee.getDepChain$internal$(), res); return res; } private static void getDependents(DepChain chain, List<VisageObject> res) { if (chain == null) return; for (Dep dep = chain.dependencies; dep != null;) { WeakBinderRef binderRef = dep.binderRef; if (binderRef != null) { if (binderRef.get() != null) { res.add(binderRef.get()); } } dep = dep.nextInBinders; } getDependents(chain.child0, res); getDependents(chain.child1, res); } } interface BinderLinkable { void setNextBinder(Dep next); }; class Dep implements BinderLinkable { /* DEBUGGING static int counter; int id = ++counter; public String toString() { return "Dep#"+id; } */ WeakBinderRef binderRef; Dep nextInBinders; public void setNextBinder(Dep next) { nextInBinders = next; } /** Back-pointer corresponding to nextInBinders. * Either the previous Dep such that * {@code ((Dep) prevInBinders).nextInBinders==this}, * or (if this is the first dep) the DepChain list head such that * {@code ((DepChain) prevInBinders).dependencies==this}. */ BinderLinkable prevInBinders; Dep nextInBindees; DepChain chain; int depNum; static Dep newDependency(VisageObject binder, int depNum) { Dep dep = new Dep(); dep.depNum = depNum; WeakBinderRef binderRef = WeakBinderRef.instance(binder); dep.binderRef = binderRef; // Link into bindee chain of binderRef Dep firstBindee = binderRef.bindees; dep.nextInBindees = firstBindee; binderRef.bindees = dep; return dep; } void linkToBindee(VisageObject bindee, int bindeeVarNum) { WeakBinderRef bref = WeakBinderRef.instance(bindee); DepChain chain = DepChain.findForce(bindeeVarNum, bindee.getDepChain$internal$(), bref); // Link into binder chain of bindee Dep firstBinder = chain.dependencies; nextInBinders = firstBinder; if (firstBinder != null) { firstBinder.prevInBinders = this; } prevInBinders = chain; chain.dependencies = this; this.chain = chain; } /** * Unlink from dependency chain of bindee. */ void unlinkFromBindee() { BinderLinkable prevBinder = prevInBinders; if (prevBinder == null) return; // Note that removeDependent might call this method while // notifyDependents is in the middle of a dependency chain. // Since notifyDependents needs the nextInBinders field, // we can't null it out, but it can be GC'd as soon as // notifyDependents gets past this Dep. // We do null out binderRef and prevInBinders to indicate // that this Dep has already been unlinked. binderRef = null; prevInBinders = null; Dep next = nextInBinders; if (prevBinder instanceof DepChain) { DepChain chain = (DepChain) prevBinder; chain.dependencies = next; if (next == null) { if (chain.child0 == null) chain.replaceParent(chain.child1); else if (chain.child1 == null) chain.replaceParent(chain.child0); } } else prevBinder.setNextBinder(next); if (next != null) { next.prevInBinders = prevBinder; } } }