/* * Copyright 2008-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 visage.animation.KeyValueTarget; /** * Pointers * * @author Brian Goetz * @author A. Sundararajan */ public class Pointer implements KeyValueTarget { private final Type type; private final VisageObject obj; private final int varnum; @org.visage.runtime.annotation.VisageSignature("(Ljava/lang/Object;)Lorg/visage/runtime/Pointer;") public static Pointer make(Type type, VisageObject obj, int varnum) { return new Pointer(type, obj, varnum); } public static boolean equals(Pointer p1, Pointer p2) { return (p1 == null) ? (p2 == null) : p1.equals(p2); } private Pointer(Type type, VisageObject obj, int varnum) { this.type = type; this.obj = obj; this.varnum = varnum; } public Object getDefaultValue() { switch (type) { case BYTE: return (byte)0; case SHORT: return (short)0; case INTEGER: return 0; case LONG: return 0L; case FLOAT: return 0.0F; case DOUBLE: return 0.0D; case BOOLEAN: return false; case SEQUENCE: return TypeInfo.Object.emptySequence; case OBJECT: return null; } // unknown type, so return null return null; } public VisageObject getVisageObject() { return obj; } public int getVarNum() { return varnum; } public Type getType() { return type; } public Pointer unwrap() { return this; } public Object get() { return obj != null? obj.get$(varnum) : getDefaultValue(); } public Object get(int pos) { assert type == Type.SEQUENCE : "expecting a sequence type"; return obj != null? obj.elem$(varnum, pos) : null; } public void set(Object value) { if (obj != null) { obj.set$(varnum, value); } } public void set(int pos, Object value) { assert type == Type.SEQUENCE : "expecting a sequence type"; if (obj != null) { obj.set$(varnum, value); } } public int size() { assert type == Type.SEQUENCE : "expecting a sequence type"; return obj != null? obj.size$(varnum) : 0; } public Object getValue() { return get(); } public void setValue(Object o) { set(o); } @Override public boolean equals(Object o) { if (o instanceof Pointer) { Pointer other = (Pointer)o; return obj == other.obj && varnum == other.varnum; } else { return false; } } @Override public int hashCode() { return System.identityHashCode(obj) ^ varnum; } public void addDependency(VisageObject dep) { if (obj != null) { obj.addDependent$(varnum, dep, 0); } } public void removeDependency(VisageObject dep) { if (obj != null) { obj.removeDependent$(varnum, dep); } } public static void switchDependence(Pointer oldPtr, Pointer newPtr, VisageObject dep, int depNum) { if (oldPtr != newPtr && dep != null) { VisageObject oldSrc = (oldPtr != null)? oldPtr.getVisageObject() : null; VisageObject newSrc = (newPtr != null)? newPtr.getVisageObject() : null; int oldVarNum = (oldPtr != null)? oldPtr.getVarNum() : 0; int newVarNum = (newPtr != null)? newPtr.getVarNum() : 0; dep.switchDependence$(oldSrc, oldVarNum, newSrc, newVarNum, depNum); } } /** * A BoundPointer is returned from the Pointer.bind(Pointer) method. * BoundPointer instance has to be kept alive till you want bind to be * effective. You can explicitly unbind the pointer using the "unbind" * method is this class. */ public static class BoundPointer extends Pointer { private Pointer srcPtr; private VisageObject listener; private BoundPointer(Pointer destPtr, Pointer srcPtr, VisageObject listener) { super(destPtr.getType(), destPtr.getVisageObject(), destPtr.getVarNum()); this.srcPtr = srcPtr; this.listener = listener; } /** * Uubind the current Pointer from the srcPtr. Repeated calls are fine * but subsequent calls are just no-ops. After unbind call, the BoundPointer * becomes effectively a regular, unbound Pointer. There is no need to * explicitly call 'unbind'. If this BoundPointer instance is unreachable, * GC will collect it and srcPtr will eventually see that the listener * object is not alive and so remove it from it's dependencies. */ public void unbind() { srcPtr.removeDependency(listener); // clear everything related to Pointer bind. srcPtr = null; listener = null; } } /** * Implements identity bind expression between "srcPtr" and the current Pointer. * Whenever the value pointed by srcPtr changes, the current Pointer's value * is set from that. * * @param srcPtr The source Pointer object to which the current Pointer is bound to */ public BoundPointer bind(Pointer srcPtr) { final VisageObject thisObj = getVisageObject(); final int thisVarNum = getVarNum(); final int srcVarNum = srcPtr.getVarNum(); VisageObject listener = new VisageBase() { @Override public boolean update$(VisageObject src, final int depNum, int startPos, int endPos, int newLength, final int phase) { if ((phase & PHASE_TRANS$PHASE) == PHASE$TRIGGER) { // update value from "src" if (thisObj != null) { thisObj.set$(thisVarNum, src.get$(srcVarNum)); } } return true; } }; // initial update from "srcPtr" this.set(thisVarNum, srcPtr.get()); // add dependency so that we will get notified with update$ calls srcPtr.addDependency(listener); // return a BoundPointer so that use can call call "unbind()" later, if needed return new BoundPointer(this, srcPtr, listener); } }