/*
 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.event;

import javafx.event.Event;
import javafx.event.EventDispatcher;

public final class EventDispatchTreeImpl implements EventDispatchTree {
    
Must be a power of two.
/** Must be a power of two. */
private static final int CAPACITY_GROWTH_FACTOR = 8; private static final int NULL_INDEX = -1; private EventDispatcher[] dispatchers; private int[] nextChildren; private int[] nextSiblings; private int reservedCount; private int rootIndex; private int tailFirstIndex; private int tailLastIndex; public EventDispatchTreeImpl() { rootIndex = NULL_INDEX; tailFirstIndex = NULL_INDEX; tailLastIndex = NULL_INDEX; } public void reset() { // shrink? for (int i = 0; i < reservedCount; ++i) { dispatchers[i] = null; } reservedCount = 0; rootIndex = NULL_INDEX; tailFirstIndex = NULL_INDEX; tailLastIndex = NULL_INDEX; } @Override public EventDispatchTree createTree() { return new EventDispatchTreeImpl(); } private boolean expandTailFirstPath; @Override public EventDispatchTree mergeTree(final EventDispatchTree tree) { if (tailFirstIndex != NULL_INDEX) { if (rootIndex != NULL_INDEX) { expandTailFirstPath = true; expandTail(rootIndex); } else { rootIndex = tailFirstIndex; } tailFirstIndex = NULL_INDEX; tailLastIndex = NULL_INDEX; } final EventDispatchTreeImpl treeImpl = (EventDispatchTreeImpl) tree; int srcLevelIndex = (treeImpl.rootIndex != NULL_INDEX) ? treeImpl.rootIndex : treeImpl.tailFirstIndex; if (rootIndex == NULL_INDEX) { rootIndex = copyTreeLevel(treeImpl, srcLevelIndex); } else { mergeTreeLevel(treeImpl, rootIndex, srcLevelIndex); } return this; } @Override public EventDispatchTree append(final EventDispatcher eventDispatcher) { ensureCapacity(reservedCount + 1); dispatchers[reservedCount] = eventDispatcher; nextSiblings[reservedCount] = NULL_INDEX; nextChildren[reservedCount] = NULL_INDEX; if (tailFirstIndex == NULL_INDEX) { tailFirstIndex = reservedCount; } else { nextChildren[tailLastIndex] = reservedCount; } tailLastIndex = reservedCount; ++reservedCount; return this; } @Override public EventDispatchTree prepend(final EventDispatcher eventDispatcher) { ensureCapacity(reservedCount + 1); dispatchers[reservedCount] = eventDispatcher; nextSiblings[reservedCount] = NULL_INDEX; nextChildren[reservedCount] = rootIndex; rootIndex = reservedCount; ++reservedCount; return this; } @Override public Event dispatchEvent(final Event event) { if (rootIndex == NULL_INDEX) { if (tailFirstIndex == NULL_INDEX) { return event; } rootIndex = tailFirstIndex; tailFirstIndex = NULL_INDEX; tailLastIndex = NULL_INDEX; } // push current state final int savedReservedCount = reservedCount; final int savedRootIndex = rootIndex; final int savedTailFirstIndex = tailFirstIndex; final int savedTailLastIndex = tailLastIndex; Event returnEvent = null; int index = rootIndex; do { rootIndex = nextChildren[index]; final Event branchReturnEvent = dispatchers[index].dispatchEvent(event, this); if (branchReturnEvent != null) { returnEvent = (returnEvent != null) ? event : branchReturnEvent; } index = nextSiblings[index]; } while (index != NULL_INDEX); // pop saved state reservedCount = savedReservedCount; rootIndex = savedRootIndex; tailFirstIndex = savedTailFirstIndex; tailLastIndex = savedTailLastIndex; return returnEvent; } @Override public String toString() { int levelIndex = (rootIndex != NULL_INDEX) ? rootIndex : tailFirstIndex; if (levelIndex == NULL_INDEX) { return "()"; } final StringBuilder sb = new StringBuilder(); appendTreeLevel(sb, levelIndex); return sb.toString(); } private void ensureCapacity(final int size) { final int newCapacity = (size + CAPACITY_GROWTH_FACTOR - 1) & ~(CAPACITY_GROWTH_FACTOR - 1); if (newCapacity == 0) { return; } if ((dispatchers == null) || (dispatchers.length < newCapacity)) { final EventDispatcher[] newDispatchers = new EventDispatcher[newCapacity]; final int[] newNextChildren = new int[newCapacity]; final int[] newNextSiblings = new int[newCapacity]; if (reservedCount > 0) { System.arraycopy(dispatchers, 0, newDispatchers, 0, reservedCount); System.arraycopy(nextChildren, 0, newNextChildren, 0, reservedCount); System.arraycopy(nextSiblings, 0, newNextSiblings, 0, reservedCount); } dispatchers = newDispatchers; nextChildren = newNextChildren; nextSiblings = newNextSiblings; } } private void expandTail(final int levelIndex) { int index = levelIndex; while (index != NULL_INDEX) { if (nextChildren[index] != NULL_INDEX) { expandTail(nextChildren[index]); } else { if (expandTailFirstPath) { nextChildren[index] = tailFirstIndex; expandTailFirstPath = false; } else { final int childLevelIndex = copyTreeLevel(this, tailFirstIndex); nextChildren[index] = childLevelIndex; } } index = nextSiblings[index]; } } private void mergeTreeLevel(final EventDispatchTreeImpl srcTree, final int dstLevelIndex, final int srcLevelIndex) { int srcIndex = srcLevelIndex; while (srcIndex != NULL_INDEX) { final EventDispatcher srcDispatcher = srcTree.dispatchers[srcIndex]; int dstIndex = dstLevelIndex; int lastDstIndex = dstLevelIndex; while ((dstIndex != NULL_INDEX) && (srcDispatcher != dispatchers[dstIndex])) { lastDstIndex = dstIndex; dstIndex = nextSiblings[dstIndex]; } if (dstIndex == NULL_INDEX) { final int siblingIndex = copySubtree(srcTree, srcIndex); nextSiblings[lastDstIndex] = siblingIndex; nextSiblings[siblingIndex] = NULL_INDEX; } else { int nextDstLevelIndex = nextChildren[dstIndex]; final int nextSrcLevelIndex = getChildIndex(srcTree, srcIndex); if (nextDstLevelIndex != NULL_INDEX) { mergeTreeLevel(srcTree, nextDstLevelIndex, nextSrcLevelIndex); } else { nextDstLevelIndex = copyTreeLevel(srcTree, nextSrcLevelIndex); nextChildren[dstIndex] = nextDstLevelIndex; } } srcIndex = srcTree.nextSiblings[srcIndex]; } } private int copyTreeLevel(final EventDispatchTreeImpl srcTree, final int srcLevelIndex) { if (srcLevelIndex == NULL_INDEX) { return NULL_INDEX; } int srcIndex = srcLevelIndex; final int dstLevelIndex = copySubtree(srcTree, srcIndex); int lastDstIndex = dstLevelIndex; srcIndex = srcTree.nextSiblings[srcIndex]; while (srcIndex != NULL_INDEX) { int dstIndex = copySubtree(srcTree, srcIndex); nextSiblings[lastDstIndex] = dstIndex; lastDstIndex = dstIndex; srcIndex = srcTree.nextSiblings[srcIndex]; } nextSiblings[lastDstIndex] = NULL_INDEX; return dstLevelIndex; } private int copySubtree(final EventDispatchTreeImpl srcTree, final int srcIndex) { ensureCapacity(reservedCount + 1); final int dstIndex = reservedCount++; final int dstChildLevelIndex = copyTreeLevel(srcTree, getChildIndex(srcTree, srcIndex)); dispatchers[dstIndex] = srcTree.dispatchers[srcIndex]; nextChildren[dstIndex] = dstChildLevelIndex; return dstIndex; } private void appendTreeLevel(final StringBuilder sb, final int levelIndex) { sb.append('('); int index = levelIndex; appendSubtree(sb, index); index = nextSiblings[index]; while (index != NULL_INDEX) { sb.append(","); appendSubtree(sb, index); index = nextSiblings[index]; } sb.append(')'); } private void appendSubtree(final StringBuilder sb, final int index) { sb.append(dispatchers[index]); final int childIndex = getChildIndex(this, index); if (childIndex != NULL_INDEX) { sb.append("->"); appendTreeLevel(sb, childIndex); } } private static int getChildIndex(final EventDispatchTreeImpl tree, final int index) { int childIndex = tree.nextChildren[index]; if ((childIndex == NULL_INDEX) && (index != tree.tailLastIndex)) { childIndex = tree.tailFirstIndex; } return childIndex; } // void dumpInternalData() { // System.out.println("reservedCount: " + reservedCount); // System.out.println("rootIndex: " + rootIndex); // System.out.println("tailFirstIndex: " + tailFirstIndex); // System.out.println("tailLastIndex: " + tailLastIndex); // // System.out.print("dispatchers:"); // for (int i = 0; i < reservedCount; ++i) { // System.out.print(" " + dispatchers[i]); // } // System.out.println(); // // System.out.print("nextSiblings:"); // for (int i = 0; i < reservedCount; ++i) { // System.out.print(" " + nextSiblings[i]); // } // System.out.println(); // // System.out.print("nextChildren:"); // for (int i = 0; i < reservedCount; ++i) { // System.out.print(" " + nextChildren[i]); // } // System.out.println(); // } }