diff options
Diffstat (limited to 'jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/swing/TreeModelAdapter.java')
-rw-r--r-- | jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/swing/TreeModelAdapter.java | 724 |
1 files changed, 0 insertions, 724 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/swing/TreeModelAdapter.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/swing/TreeModelAdapter.java deleted file mode 100644 index 0d3ea8a683..0000000000 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/swing/TreeModelAdapter.java +++ /dev/null @@ -1,724 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2007, 2008 Oracle. All rights reserved. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0, which accompanies this distribution - * and is available at http://www.eclipse.org/legal/epl-v10.html. - * - * Contributors: - * Oracle - initial API and implementation - ******************************************************************************/ -package org.eclipse.jpt.utility.internal.model.value.swing; - -import java.util.ArrayList; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; - -import javax.swing.event.TreeModelListener; -import javax.swing.tree.TreePath; - -import org.eclipse.jpt.utility.internal.StringTools; -import org.eclipse.jpt.utility.internal.model.listener.awt.AWTListChangeListenerWrapper; -import org.eclipse.jpt.utility.internal.model.listener.awt.AWTPropertyChangeListenerWrapper; -import org.eclipse.jpt.utility.internal.model.listener.awt.AWTStateChangeListenerWrapper; -import org.eclipse.jpt.utility.internal.model.value.StaticPropertyValueModel; -import org.eclipse.jpt.utility.model.event.ListChangeEvent; -import org.eclipse.jpt.utility.model.event.PropertyChangeEvent; -import org.eclipse.jpt.utility.model.event.StateChangeEvent; -import org.eclipse.jpt.utility.model.listener.ListChangeListener; -import org.eclipse.jpt.utility.model.listener.PropertyChangeListener; -import org.eclipse.jpt.utility.model.listener.StateChangeListener; -import org.eclipse.jpt.utility.model.value.ListValueModel; -import org.eclipse.jpt.utility.model.value.PropertyValueModel; -import org.eclipse.jpt.utility.model.value.TreeNodeValueModel; - -/** - * This javax.swing.tree.TreeModel can be used to keep a TreeModelListener - * (e.g. a JTree) in synch with a tree of TreeNodeValueModel objects. Unlike - * javax.swing.tree.DefaultTreeModel, you do not add and remove nodes with - * methods implemented here. You can add and remove nodes by adding and - * removing them directly to/from the nodes (or, more typically, the domain - * objects the nodes are wrapping and listening to). - * - * Due to limitations in JTree, the root of the tree can never be null, - * which, typically, should not be a problem. (If you want to display an empty - * tree you can set the JTree's treeModel to null.) - */ -public class TreeModelAdapter<T> - extends AbstractTreeModel -{ - /** - * A value model on the underlying tree's root node and its - * corresponding listener. This allows clients to swap out - * the entire tree. Due to limitations in JTree, the root should - * never be set to null while we have listeners. - */ - private final PropertyValueModel<TreeNodeValueModel<T>> rootHolder; - private final PropertyChangeListener rootListener; - - /** - * A listener that notifies us when a node's internal - * "state" changes (as opposed to the node's value or list of - * children), allowing us to forward notification to our listeners. - */ - private final StateChangeListener nodeStateListener; - - /** - * A listener that notifies us when a node's "value" - * changes (as opposed to the node's state or list of - * children), allowing us to forward notification to our listeners. - * Typically, this will only happen with nodes that hold - * primitive data. - */ - private final PropertyChangeListener nodeValueListener; - - /** - * A listener that notifies us when an underlying node's - * "list" of children changes, allowing us to keep our - * internal tree in synch with the underlying tree model. - */ - private final ListChangeListener childrenListener; - - /* these attributes make up our internal tree */ - /** - * The root cannot be null while we have listeners, which is - * most of the time. The root is cached so we can disengage - * from it when it has been swapped out. - */ - private TreeNodeValueModel<T> root; - - /** - * Map the nodes to their lists of children. - * We cache these so we can swap out the entire list of children - * when we receive a #listChanged() event (which does not include - * the items that were affected). - * @see EventChangePolicy#rebuildChildren() - */ - final IdentityHashMap<TreeNodeValueModel<T>, List<TreeNodeValueModel<T>>> childrenLists; - - /** - * Map the children models to their parents. - * We cache these so we can figure out the "real" source of the - * list change events (the parent). - * @see EventChangePolicy#parent() - */ - final IdentityHashMap<ListValueModel<TreeNodeValueModel<T>>, TreeNodeValueModel<T>> parents; - - - // ********** constructors ********** - - /** - * Construct a tree model for the specified root. - */ - public TreeModelAdapter(PropertyValueModel<TreeNodeValueModel<T>> rootHolder) { - super(); - if (rootHolder == null) { - throw new NullPointerException(); - } - this.rootHolder = rootHolder; - this.rootListener = this.buildRootListener(); - this.nodeStateListener = this.buildNodeStateListener(); - this.nodeValueListener = this.buildNodeValueListener(); - this.childrenListener = this.buildChildrenListener(); - this.childrenLists = new IdentityHashMap<TreeNodeValueModel<T>, List<TreeNodeValueModel<T>>>(); - this.parents = new IdentityHashMap<ListValueModel<TreeNodeValueModel<T>>, TreeNodeValueModel<T>>(); - } - - /** - * Construct a tree model for the specified root. - */ - public TreeModelAdapter(TreeNodeValueModel<T> root) { - this(new StaticPropertyValueModel<TreeNodeValueModel<T>>(root)); - } - - - // ********** initialization ********** - - protected PropertyChangeListener buildRootListener() { - return new AWTPropertyChangeListenerWrapper(this.buildRootListener_()); - } - - protected PropertyChangeListener buildRootListener_() { - return new PropertyChangeListener() { - public void propertyChanged(PropertyChangeEvent event) { - TreeModelAdapter.this.rootChanged(); - } - @Override - public String toString() { - return "root listener"; - } - }; - } - - protected PropertyChangeListener buildNodeValueListener() { - return new AWTPropertyChangeListenerWrapper(this.buildNodeValueListener_()); - } - - protected PropertyChangeListener buildNodeValueListener_() { - return new PropertyChangeListener() { - @SuppressWarnings("unchecked") - public void propertyChanged(PropertyChangeEvent event) { - TreeModelAdapter.this.nodeChanged((TreeNodeValueModel<T>) event.getSource()); - } - @Override - public String toString() { - return "node value listener"; - } - }; - } - - protected StateChangeListener buildNodeStateListener() { - return new AWTStateChangeListenerWrapper(this.buildNodeStateListener_()); - } - - protected StateChangeListener buildNodeStateListener_() { - return new StateChangeListener() { - @SuppressWarnings("unchecked") - public void stateChanged(StateChangeEvent event) { - TreeModelAdapter.this.nodeChanged((TreeNodeValueModel<T>) event.getSource()); - } - @Override - public String toString() { - return "node state listener"; - } - }; - } - - protected ListChangeListener buildChildrenListener() { - return new AWTListChangeListenerWrapper(this.buildChildrenListener_()); - } - - protected ListChangeListener buildChildrenListener_() { - return new ListChangeListener() { - public void itemsAdded(ListChangeEvent event) { - new EventChangePolicy(event).addChildren(); - } - public void itemsRemoved(ListChangeEvent event) { - new EventChangePolicy(event).removeChildren(); - } - public void itemsReplaced(ListChangeEvent event) { - new EventChangePolicy(event).replaceChildren(); - } - public void itemsMoved(ListChangeEvent event) { - new EventChangePolicy(event).moveChildren(); - } - public void listCleared(ListChangeEvent event) { - new EventChangePolicy(event).clearChildren(); - } - public void listChanged(ListChangeEvent event) { - new EventChangePolicy(event).rebuildChildren(); - } - @Override - public String toString() { - return "children listener"; - } - }; - } - - - // ********** TreeModel implementation ********** - - public Object getRoot() { - return this.root; - } - - @SuppressWarnings("unchecked") - public Object getChild(Object parent, int index) { - return ((TreeNodeValueModel<T>) parent).child(index); - } - - @SuppressWarnings("unchecked") - public int getChildCount(Object parent) { - return ((TreeNodeValueModel<T>) parent).childrenSize(); - } - - @SuppressWarnings("unchecked") - public boolean isLeaf(Object node) { - return ((TreeNodeValueModel<T>) node).isLeaf(); - } - - @SuppressWarnings("unchecked") - public void valueForPathChanged(TreePath path, Object newValue) { - ((TreeNodeValueModel<T>) path.getLastPathComponent()).setValue((T) newValue); - } - - @SuppressWarnings("unchecked") - public int getIndexOfChild(Object parent, Object child) { - return ((TreeNodeValueModel<T>) parent).indexOfChild((TreeNodeValueModel<T>) child); - } - - /** - * Extend to start listening to the underlying model if necessary. - */ - @Override - public void addTreeModelListener(TreeModelListener l) { - if (this.hasNoTreeModelListeners()) { - this.engageModel(); - } - super.addTreeModelListener(l); - } - - /** - * Extend to stop listening to the underlying model if appropriate. - */ - @Override - public void removeTreeModelListener(TreeModelListener l) { - super.removeTreeModelListener(l); - if (this.hasNoTreeModelListeners()) { - this.disengageModel(); - } - } - - - // ********** behavior ********** - - /** - * Listen to the root and all the other nodes - * in the underlying tree model. - */ - private void engageModel() { - this.rootHolder.addPropertyChangeListener(PropertyValueModel.VALUE, this.rootListener); - this.root = this.rootHolder.getValue(); - if (this.root == null) { - throw new NullPointerException(); // the root cannot be null while we have listeners - } - this.engageNode(this.root); - this.addRoot(); - } - - /** - * Add the root and all of the nodes to the underlying tree. - */ - private void addRoot() { - this.addNode(0, this.root); - } - - /** - * Stop listening to the root and all the other - * nodes in the underlying tree model. - */ - private void disengageModel() { - this.removeRoot(); - this.disengageNode(this.root); - this.root = null; - this.rootHolder.removePropertyChangeListener(PropertyValueModel.VALUE, this.rootListener); - } - - /** - * Remove the root and all of the nodes from the underlying tree. - */ - private void removeRoot() { - this.removeNode(0, this.root); - } - - /** - * The root has been swapped. - * This method is a bit gnarly because the API for notifying listeners - * that the root has changed is a bit inconsistent with that used for - * non-root nodes. - */ - void rootChanged() { - TreeNodeValueModel<T> newRoot = this.rootHolder.getValue(); - if (newRoot == null) { - throw new NullPointerException(); // the root cannot be null while we have listeners - } - // remove all the current root's children from the tree - // and remove the it from the internal tree - this.removeRoot(); - - // save the old root and swap in the new root - TreeNodeValueModel<T> oldRoot = this.root; - this.root = newRoot; - - // we must be listening to both the old and new roots when we fire the event - // because their values can be affected by whether they have listeners - this.engageNode(this.root); - this.fireTreeRootReplaced(this.root); - // now we can stop listening to the old root - this.disengageNode(oldRoot); - - // add the new root to the internal tree and - // add all its children to the tree also - this.addRoot(); - } - - /** - * Either the "value" or the "state" of the specified node has changed, - * forward notification to our listeners. - */ - void nodeChanged(TreeNodeValueModel<T> node) { - TreeNodeValueModel<T> parent = node.parent(); - if (parent == null) { - this.fireTreeRootChanged(node); - } else { - this.fireTreeNodeChanged(parent.path(), parent.indexOfChild(node), node); - } - } - - /** - * Listen to the nodes, notify our listeners that the nodes were added, - * and then add the nodes to our internal tree. - * We must listen to the nodes before notifying anybody, because - * adding a listener can change the value of a node. - */ - void addChildren(TreeNodeValueModel<T>[] path, int[] childIndices, TreeNodeValueModel<T>[] children) { - int len = childIndices.length; - for (int i = 0; i < len; i++) { - this.engageNode(children[i]); - } - this.fireTreeNodesInserted(path, childIndices, children); - for (int i = 0; i < len; i++) { - this.addNode(childIndices[i], children[i]); - } - } - - /** - * Listen to the node and its children model. - */ - private void engageNode(TreeNodeValueModel<T> node) { - node.addStateChangeListener(this.nodeStateListener); - node.addPropertyChangeListener(PropertyValueModel.VALUE, this.nodeValueListener); - node.childrenModel().addListChangeListener(ListValueModel.LIST_VALUES, this.childrenListener); - } - - /** - * Add the node to our internal tree; - * then recurse down through the node's children, - * adding them to the internal tree also. - */ - private void addNode(int index, TreeNodeValueModel<T> node) { - this.addNodeToInternalTree(node.parent(), index, node, node.childrenModel()); - new NodeChangePolicy(node).addChildren(); - } - - /** - * Add the specified node to our internal tree. - */ - private void addNodeToInternalTree(TreeNodeValueModel<T> parent, int index, TreeNodeValueModel<T> node, ListValueModel<TreeNodeValueModel<T>> childrenModel) { - List<TreeNodeValueModel<T>> siblings = this.childrenLists.get(parent); - if (siblings == null) { - siblings = new ArrayList<TreeNodeValueModel<T>>(); - this.childrenLists.put(parent, siblings); - } - siblings.add(index, node); - - this.parents.put(childrenModel, node); - } - - /** - * Remove nodes from our internal tree, notify our listeners that the - * nodes were removed, then stop listening to the nodes. - * We must listen to the nodes until after notifying anybody, because - * removing a listener can change the value of a node. - */ - void removeChildren(TreeNodeValueModel<T>[] path, int[] childIndices, TreeNodeValueModel<T>[] children) { - int len = childIndices.length; - for (int i = 0; i < len; i++) { - // the indices slide down a notch each time we remove a child - this.removeNode(childIndices[i] - i, children[i]); - } - this.fireTreeNodesRemoved(path, childIndices, children); - for (int i = 0; i < len; i++) { - this.disengageNode(children[i]); - } - } - - /** - * First, recurse down through the node's children, - * removing them from our internal tree; - * then remove the node itself from our internal tree. - */ - private void removeNode(int index, TreeNodeValueModel<T> node) { - new NodeChangePolicy(node).removeChildren(); - this.removeNodeFromInternalTree(node.parent(), index, node, node.childrenModel()); - } - - /** - * Remove the specified node from our internal tree. - */ - private void removeNodeFromInternalTree(TreeNodeValueModel<T> parent, int index, TreeNodeValueModel<T> node, ListValueModel<TreeNodeValueModel<T>> childrenModel) { - this.parents.remove(childrenModel); - - List<TreeNodeValueModel<T>> siblings = this.childrenLists.get(parent); - siblings.remove(index); - if (siblings.isEmpty()) { - this.childrenLists.remove(parent); - } - } - - /** - * Stop listening to the node and its children model. - */ - private void disengageNode(TreeNodeValueModel<T> node) { - node.childrenModel().removeListChangeListener(ListValueModel.LIST_VALUES, this.childrenListener); - node.removePropertyChangeListener(PropertyValueModel.VALUE, this.nodeValueListener); - node.removeStateChangeListener(this.nodeStateListener); - } - - void moveChildren(TreeNodeValueModel<T> parent, int targetIndex, int sourceIndex, int length) { - List<TreeNodeValueModel<T>> childrenList = this.childrenLists.get(parent); - ArrayList<TreeNodeValueModel<T>> temp = new ArrayList<TreeNodeValueModel<T>>(length); - for (int i = 0; i < length; i++) { - temp.add(childrenList.remove(sourceIndex)); - } - childrenList.addAll(targetIndex, temp); - - this.fireTreeStructureChanged(parent.path()); - } - - - // ********** standard methods ********** - - @Override - public String toString() { - return StringTools.buildToStringFor(this, this.root); - } - - - // ********** inner classes ********** - - /** - * Coalesce some of the common change policy behavior. - */ - private abstract class ChangePolicy { - - ChangePolicy() { - super(); - } - - /** - * Add the current set of children. - */ - void addChildren() { - TreeModelAdapter.this.addChildren(this.parent().path(), this.childIndices(), this.childArray()); - } - - /** - * Remove the current set of children. - */ - void removeChildren() { - TreeModelAdapter.this.removeChildren(this.parent().path(), this.childIndices(), this.childArray()); - } - - /** - * Return an array of the indices of the current set of children, - * which should be contiguous. - */ - int[] childIndices() { - return this.buildIndices(this.childrenStartIndex(), this.childrenSize()); - } - - /** - * Return an array of the current set of children. - */ - TreeNodeValueModel<T>[] childArray() { - return this.buildArray(this.children(), this.childrenSize()); - } - - /** - * Build an array to hold the elements in the specified iterator. - * If they are different sizes, something is screwed up... - */ - TreeNodeValueModel<T>[] buildArray(Iterator<TreeNodeValueModel<T>> stream, int size) { - @SuppressWarnings("unchecked") - TreeNodeValueModel<T>[] array = new TreeNodeValueModel[size]; - for (int i = 0; stream.hasNext(); i++) { - array[i] = stream.next(); - } - return array; - } - - /** - * Return a set of indices, starting at zero and - * continuing for the specified size. - */ - int[] buildIndices(int size) { - return buildIndices(0, size); - } - - /** - * Return a set of indices, starting at the specified index and - * continuing for the specified size. - */ - int[] buildIndices(int start, int size) { - int[] indices = new int[size]; - int index = start; - for (int i = 0; i < size; i++) { - indices[i] = index++; - } - return indices; - } - - /** - * Return the parent of the current set of children. - */ - abstract TreeNodeValueModel<T> parent(); - - /** - * Return the starting index for the current set of children. - */ - abstract int childrenStartIndex(); - - /** - * Return the size of the current set of children. - */ - abstract int childrenSize(); - - /** - * Return an interator on the current set of children. - */ - abstract Iterator<TreeNodeValueModel<T>> children(); - - } - - - /** - * Wraps a ListChangeEvent for adding, removing, replacing, - * and changing children. - */ - private class EventChangePolicy extends ChangePolicy { - private ListChangeEvent event; - - EventChangePolicy(ListChangeEvent event) { - this.event = event; - } - - /** - * Map the ListChangeEvent's source to the corresponding parent. - */ - @Override - TreeNodeValueModel<T> parent() { - return TreeModelAdapter.this.parents.get(this.event.getSource()); - } - - /** - * The ListChangeEvent's item index is the children start index. - */ - @Override - int childrenStartIndex() { - return this.event.getIndex(); - } - - /** - * The ListChangeEvent's size is the children size. - */ - @Override - int childrenSize() { - return this.event.itemsSize(); - } - - /** - * The ListChangeEvent's items are the children. - */ - @Override - @SuppressWarnings("unchecked") - Iterator<TreeNodeValueModel<T>> children() { - return (Iterator<TreeNodeValueModel<T>>) this.event.items(); - } - - /** - * Remove the old nodes and add the new ones. - */ - void replaceChildren() { - TreeNodeValueModel<T>[] parentPath = this.parent().path(); - int[] childIndices = this.childIndices(); - TreeModelAdapter.this.removeChildren(parentPath, childIndices, this.replacedChildren()); - TreeModelAdapter.this.addChildren(parentPath, childIndices, this.childArray()); - } - - /** - * Remove the old nodes and add the new ones. - */ - void moveChildren() { - TreeModelAdapter.this.moveChildren(this.parent(), this.event.getTargetIndex(), this.event.getSourceIndex(), this.event.getMoveLength()); - } - - /** - * Clear all the nodes. - */ - void clearChildren() { - TreeNodeValueModel<T> parent = this.parent(); - TreeNodeValueModel<T>[] parentPath = parent.path(); - List<TreeNodeValueModel<T>> childrenList = TreeModelAdapter.this.childrenLists.get(parent); - int[] childIndices = this.buildIndices(childrenList.size()); - TreeNodeValueModel<T>[] childArray = this.buildArray(childrenList.iterator(), childrenList.size()); - TreeModelAdapter.this.removeChildren(parentPath, childIndices, childArray); - } - - /** - * Remove all the old nodes and add all the new nodes. - */ - void rebuildChildren() { - TreeNodeValueModel<T> parent = this.parent(); - TreeNodeValueModel<T>[] parentPath = parent.path(); - List<TreeNodeValueModel<T>> childrenList = TreeModelAdapter.this.childrenLists.get(parent); - int[] childIndices = this.buildIndices(childrenList.size()); - TreeNodeValueModel<T>[] childArray = this.buildArray(childrenList.iterator(), childrenList.size()); - TreeModelAdapter.this.removeChildren(parentPath, childIndices, childArray); - - childIndices = this.buildIndices(parent.childrenModel().size()); - childArray = this.buildArray(parent.childrenModel().iterator(), parent.childrenSize()); - TreeModelAdapter.this.addChildren(parentPath, childIndices, childArray); - } - - /** - * The ListChangeEvent's replaced items are the replaced children. - */ - @SuppressWarnings("unchecked") - TreeNodeValueModel<T>[] replacedChildren() { - return this.buildArray((Iterator<TreeNodeValueModel<T>>) this.event.replacedItems(), this.event.itemsSize()); - } - - } - - - /** - * Wraps a TreeNodeValueModel for adding and removing its children. - */ - private class NodeChangePolicy extends ChangePolicy { - private TreeNodeValueModel<T> node; - - NodeChangePolicy(TreeNodeValueModel<T> node) { - this.node = node; - } - - /** - * The node itself is the parent. - */ - @Override - TreeNodeValueModel<T> parent() { - return this.node; - } - - /** - * Since we will always be dealing with all of the node's - * children, the children start index is always zero. - */ - @Override - int childrenStartIndex() { - return 0; - } - - /** - * Since we will always be dealing with all of the node's - * children, the children size is always equal to the size - * of the children model. - */ - @Override - int childrenSize() { - return this.node.childrenModel().size(); - } - - /** - * Since we will always be dealing with all of the node's - * children, the children are all the objects held by - * the children model. - */ - @Override - Iterator<TreeNodeValueModel<T>> children() { - return this.node.childrenModel().iterator(); - } - - } - -} |