/* * Copyright (c) 2009, SQL Power Group Inc. * * This file is part of Wabit. * * Wabit is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Wabit 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ca.sqlpower.swingui.object; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.event.TreeModelListener; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import ca.sqlpower.object.SPObject; import ca.sqlpower.object.WorkspaceGraphModel; import ca.sqlpower.util.SQLPowerUtils; /** * Displays a WorkspaceGraphModel as a tree. Each node in the tree is wrapped in * a {@link WorkspaceGraphTreeNodeWrapper} object in case it needs to be placed * in the tree multiple times. This allows two different objects in the graph to * point to the same object. If there is a cycle in the graph the cycle will be * broken when an object in the cycle is reached that appears as a parent to it * in the tree. */ public class WorkspaceGraphTreeModel implements TreeModel { /** * Contains all of the current listeners on this model. */ private final List<TreeModelListener> treeModelListeners = new ArrayList<TreeModelListener>(); /** * This is the root of the tree this tree model is representing. */ private WorkspaceGraphTreeNodeWrapper rootTreeNode; private final boolean skipAncestors; private final List<Class<? extends SPObject>> skipObjects; /** * Constructs a tree model based on the graph given to it. */ public WorkspaceGraphTreeModel(WorkspaceGraphModel model) { this(model, false, Collections.<Class<? extends SPObject>>emptyList()); } public WorkspaceGraphTreeModel(WorkspaceGraphModel model, boolean skipAncestors, List<Class<? extends SPObject>> skipObjects) { this.skipAncestors = skipAncestors; this.skipObjects = skipObjects; rootTreeNode = addNodeToTree(null, model.getGraphStartNode(), model); } /** * Helper method for the constructor to recursively build the tree from the * given graph. * * @return The node returned is the tree node created by the call to this * method. If the node cannot be made as it would make an infinite * cycle in the tree null is returned. */ private WorkspaceGraphTreeNodeWrapper addNodeToTree( WorkspaceGraphTreeNodeWrapper parent, SPObject nodeToAdd, WorkspaceGraphModel graph) { //if it exists in its parent chain continue WorkspaceGraphTreeNodeWrapper ancestor = parent; while (ancestor != null) { if (ancestor.getWrappedObject().equals(nodeToAdd)) { return null; } ancestor = ancestor.getParent(); } if (skipAncestors && (SQLPowerUtils.getAncestorList(graph.getGraphStartNode()).contains(nodeToAdd))) { return null; } if (skipObjects.contains(nodeToAdd.getClass())) { for (SPObject child : graph.getAdjacentNodes(nodeToAdd)) { addNodeToTree(parent, child, graph); } return null; } WorkspaceGraphTreeNodeWrapper newTreeNode = new WorkspaceGraphTreeNodeWrapper(nodeToAdd); if (parent != null) { parent.addChild(newTreeNode); } for (SPObject child : graph.getAdjacentNodes(nodeToAdd)) { addNodeToTree(newTreeNode, child, graph); } return newTreeNode; } public void addTreeModelListener(TreeModelListener l) { treeModelListeners.add(l); } public void removeTreeModelListener(TreeModelListener l) { treeModelListeners.remove(l); } public Object getChild(Object parent, int index) { return ((WorkspaceGraphTreeNodeWrapper) parent).getChild(index); } public int getChildCount(Object parent) { return ((WorkspaceGraphTreeNodeWrapper) parent).getChildren().size(); } public int getIndexOfChild(Object parent, Object child) { return ((WorkspaceGraphTreeNodeWrapper) parent).getIndexOfChild( (WorkspaceGraphTreeNodeWrapper) child); } public Object getRoot() { return rootTreeNode; } public boolean isLeaf(Object node) { return ((WorkspaceGraphTreeNodeWrapper) node).getChildren().isEmpty(); } public void valueForPathChanged(TreePath path, Object newValue) { throw new IllegalStateException("This tree represents a graph and cannot be " + "changed unless the underlying graph is changed."); } }