- protected Box left = null;
- protected Box right = null;
- protected Box rootChild = null;
- protected Box peerTree_parent = null;
- public abstract Box peerTree_leftmost();
- public abstract Box peerTree_rightmost();
- public abstract Box insertBeforeMe(Box cell);
- public abstract Box insertAfterMe(Box cell);
- protected abstract Box fixAfterInsertion();
- protected abstract Box fixAfterDeletion();
- protected abstract Box rotateLeft();
- protected abstract Box rotateRight();
- protected abstract int numPeerChildren();
+
+ // Tree Handling //////////////////////////////////////////////////////////////////////
+
+ public final int getIndexInParent() { return parent == null ? 0 : parent.indexNode(this); }
+ public final Box nextSibling() { return parent == null ? null : parent.getChild(parent.indexNode(this) + 1); }
+ public final Box prevSibling() { return parent == null ? null : parent.getChild(parent.indexNode(this) - 1); }
+ public final Box getChild(int i) {
+ if (i < 0) return null;
+ if (i >= treeSize()) return null;
+ return (Box)getNode(i);
+ }
+
+ // Tree Manipulation /////////////////////////////////////////////////////////////////////
+
+ /** remove the i^th child */
+ public void removeChild(int i) {
+ Box b = getChild(i);
+ MARK_REFLOW_b;
+ b.dirty();
+ b.clear(MOUSEINSIDE);
+ deleteNode(i);
+ b.parent = null;
+ MARK_REFLOW;
+ putAndTriggerTraps("childremoved", b);
+ }
+
+ public void put(int i, Object value) throws JSExn {
+ if (i < 0) return;
+
+ if (value != null && !(value instanceof Box)) {
+ if (Log.on) Log.logJS(this, "attempt to set a numerical property on a box to a non-box");
+ return;
+ }
+
+ if (redirect == null) {
+ if (value == null) putAndTriggerTraps("childremoved", getChild(i));
+ else Log.logJS(this, "attempt to add/remove children to/from a node with a null redirect");
+
+ } else if (redirect != this) {
+ if (value != null) putAndTriggerTraps("childadded", value);
+ redirect.put(i, value);
+ if (value == null) {
+ Box b = (Box)redirect.get(new Integer(i));
+ if (b != null) putAndTriggerTraps("childremoved", b);
+ }
+
+ } else if (value == null) {
+ if (i < 0 || i > treeSize()) return;
+ Box b = getChild(i);
+ removeChild(i);
+ putAndTriggerTraps("childremoved", b);
+
+ } else {
+ Box b = (Box)value;
+
+ // check if box being moved is currently target of a redirect
+ for(Box cur = b.parent; cur != null; cur = cur.parent)
+ if (cur.redirect == b) {
+ if (Log.on) Log.logJS(this, "attempt to move a box that is the target of a redirect");
+ return;
+ }
+
+ // check for recursive ancestor violation
+ for(Box cur = this; cur != null; cur = cur.parent)
+ if (cur == b) {
+ if (Log.on) Log.logJS(this, "attempt to make a node a parent of its own ancestor");
+ if (Log.on) Log.log(this, "box == " + this + " ancestor == " + b);
+ return;
+ }
+
+ if (b.parent != null) b.parent.removeChild(b.parent.indexNode(b));
+ insertNode(i, b);
+ b.parent = this;
+
+ // need both of these in case child was already uncalc'ed
+ MARK_REFLOW_b;
+ MARK_REFLOW;
+
+ b.dirty();
+ putAndTriggerTraps("childadded", b);
+ }
+ }
+
+
+ public final void putAndTriggerTraps(Object key, Object value) {
+ try {
+ super.putAndTriggerTraps(key, value);
+ } catch (JSExn jse) {
+ Log.logJS("attempt to put value " + value + " to key " + key + " on a box triggered a trap which threw:");
+ Log.logJS(jse);
+ }
+ }
+
+ public final Object getAndTriggerTraps(Object key) {
+ try {
+ return super.getAndTriggerTraps(key);
+ } catch (JSExn jse) {
+ Log.logJS("attempt to get key " + key + " on a box triggered a trap which threw:");
+ Log.logJS(jse);
+ return null;
+ }
+ }