package de.uni_passau.fim.infosun.prophet.experimentEditor.qTree; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import de.uni_passau.fim.infosun.prophet.util.qTree.QTreeNode; /** * A <code>TreeModel</code> that handles <code>QTreeNode</code> objects. */ public class QTreeModel implements TreeModel { private List<TreeModelListener> listeners; private QTreeNode root; /** * Constructs a new <code>QTreeModel</code> with the given root for the tree. * * @param root * the root */ public QTreeModel(QTreeNode root) { listeners = new ArrayList<>(); this.root = root; } /** * Sets the root of the tree. * * @param newRoot * the new root of the tree * @throws NullPointerException if <code>newRoot</code> is <code>null</code> */ public void setRoot(QTreeNode newRoot) { Objects.requireNonNull(newRoot, "newRoot must not be null!"); QTreeNode oldRoot = root; root = newRoot; fireTreeStructureChanged(oldRoot); } /** * Renames a node to the new name. * * @param renameNode the node to be renamed * @param newName the new name for the node */ public void rename(QTreeNode renameNode, String newName) { renameNode.setName(newName); fireNodeChanged(renameNode); } /** * Fires a <code>TreeModelEvent</code> indicating that the given node has changed. * * @param changedNode the node that has changed */ private void fireNodeChanged(QTreeNode changedNode) { Object[] path; Object[] children = {changedNode}; int[] childIndices; QTreeNode parent = changedNode.getParent(); if (parent == null) { // construct a NodeChanged event for the root node path = new Object[] {changedNode}; childIndices = null; } else { path = buildPath(changedNode, false); childIndices = new int[] {parent.getIndexOfChild(changedNode)}; } TreeModelEvent event = new TreeModelEvent(this, path, childIndices, children); for (TreeModelListener tml : listeners) { tml.treeNodesChanged(event); } } /** * Removes the given node from its parent thereby deleting it from the tree. * * @param removeNode the node to be removed */ public void removeFromParent(QTreeNode removeNode) { QTreeNode parent = removeNode.getParent(); int oldIndex = parent.getIndexOfChild(removeNode); parent.removeChild(removeNode); fireNodeRemoved(removeNode, oldIndex); } /** * Fires a <code>TreeModelEvent</code> indicating that the given node was removed from its parent. * * @param removedNode the node that was removed * @param oldIndex the index of the node in its parent before it was removed */ private void fireNodeRemoved(QTreeNode removedNode, int oldIndex) { Object[] path = buildPath(removedNode, false); Object[] children = {removedNode}; int[] childIndices = {oldIndex}; TreeModelEvent event = new TreeModelEvent(this, path, childIndices, children); for (TreeModelListener tml : listeners) { tml.treeNodesRemoved(event); } } /** * Adds a child node to the given parent. * * @param parent * the parent of the child * @param child * the child node to be added */ public void addChild(QTreeNode parent, QTreeNode child) { parent.addChild(child); fireNodeInserted(child); } /** * Adds a child node to the given parent at the given index. * * @param parent * the parent of the child * @param child * the child node to be added * @param index * the index for the child */ public void addChild(QTreeNode parent, QTreeNode child, int index) { parent.addChild(child, index); fireNodeInserted(child); } /** * Fires a <code>TreeModelEvent</code> indicating that the given node has been inserted. * * @param insertedNode * the node that has been inserted */ private void fireNodeInserted(QTreeNode insertedNode) { Object[] path = buildPath(insertedNode, false); Object[] children = {insertedNode}; int[] childIndices = {insertedNode.getParent().getIndexOfChild(insertedNode)}; TreeModelEvent event = new TreeModelEvent(this, path, childIndices, children); for (TreeModelListener tml : listeners) { tml.treeNodesInserted(event); } } /** * Builds an array of <code>QTreeNode</code>s that uniquely identify the path to the given node in the tree. * The elements of the array are ordered with the root as the first element of the array. * * @param from * the node for which the path is to be built * @param includeFrom * whether to include the <code>QTreeNode</code> <code>from</code> as the last element of the array * * @return the path in the specified format */ public static QTreeNode[] buildPath(QTreeNode from, boolean includeFrom) { ArrayList<QTreeNode> path = new ArrayList<>(); QTreeNode node = from.getParent(); if (includeFrom) { path.add(from); } while (node != null) { path.add(node); node = node.getParent(); } Collections.reverse(path); return path.toArray(new QTreeNode[path.size()]); } /** * Fires a <code>TreeModelEvent</code> indicating that the whole subtree under <code>root</code> has changed. * Passing <code>null</code> as <code>root</code> indicates that the tree has a new root node when there was none * before. * * @param root * the root of the subtree */ private void fireTreeStructureChanged(QTreeNode root) { TreeModelEvent event; if (root != null) { event = new TreeModelEvent(this, buildPath(root, true)); } else { event = new TreeModelEvent(this, new Object[] {this.root}); } for (TreeModelListener tml : listeners) { tml.treeStructureChanged(event); } } @Override public QTreeNode getRoot() { return root; } @Override public QTreeNode getChild(Object parent, int index) { return ((QTreeNode) parent).getChild(index); } @Override public int getChildCount(Object parent) { return ((QTreeNode) parent).getChildCount(); } @Override public boolean isLeaf(Object node) { return ((QTreeNode) node).isLeaf(); } @Override public void valueForPathChanged(TreePath path, Object newValue) { // not used by this model } @Override public int getIndexOfChild(Object parent, Object child) { return ((QTreeNode) parent).getIndexOfChild((QTreeNode) child); } @Override public void addTreeModelListener(TreeModelListener l) { listeners.add(l); } @Override public void removeTreeModelListener(TreeModelListener l) { listeners.remove(l); } }