Revision: 3206
Author: [email protected]
Date: Tue Dec 15 13:50:36 2009
Log: SQLTables now have folders in the tree model. These folders do not actually exist in the model but are just for UI. This may have introduced a bug where deleting columns from the tree tries to delete additional objects. This will be investigated shortly.
http://code.google.com/p/power-architect/source/detail?r=3206

Modified:
/branches/sqlobject-spobject-model/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java

=======================================
--- /branches/sqlobject-spobject-model/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java Tue Dec 1 14:21:17 2009 +++ /branches/sqlobject-spobject-model/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java Tue Dec 15 13:50:36 2009
@@ -21,6 +21,7 @@
 import java.beans.PropertyChangeEvent;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -37,20 +38,245 @@

 import org.apache.log4j.Logger;

-import ca.sqlpower.object.AbstractSPListener;
 import ca.sqlpower.object.SPChildEvent;
+import ca.sqlpower.object.SPListener;
 import ca.sqlpower.object.SPObject;
+import ca.sqlpower.sqlobject.SQLColumn;
+import ca.sqlpower.sqlobject.SQLIndex;
 import ca.sqlpower.sqlobject.SQLObject;
 import ca.sqlpower.sqlobject.SQLObjectException;
 import ca.sqlpower.sqlobject.SQLObjectRoot;
 import ca.sqlpower.sqlobject.SQLRelationship;
+import ca.sqlpower.sqlobject.SQLTable;
 import ca.sqlpower.util.SQLPowerUtils;
 import ca.sqlpower.util.TransactionEvent;

-public class DBTreeModel extends AbstractSPListener implements TreeModel, java.io.Serializable {
+public class DBTreeModel implements TreeModel, java.io.Serializable {

        private static Logger logger = Logger.getLogger(DBTreeModel.class);

+       /**
+        * A visual node for the tree that groups children of the table 
together.
+        */
+       private static class FolderNode extends SQLObject {
+
+           private final SQLTable parentTable;
+        private final Class<? extends SQLObject> containingChildType;
+
+        /**
+         * @param parentTable
+         *            The SQLTable that this folder is to appear under
+         * @param containingChildType
+         *            The type of child of the SQLTable this folder is to
+         *            contain. Must be a valid type of child in the table.
+         */
+ public FolderNode(SQLTable parentTable, Class<? extends SQLObject> containingChildType) {
+               this.parentTable = parentTable;
+ if (!parentTable.getAllowedChildTypes().contains(containingChildType)) + throw new IllegalArgumentException(containingChildType + " is not a valid child type of " + parentTable);
+            this.containingChildType = containingChildType;
+           }
+
+           public SQLTable getParentTable() {
+            return parentTable;
+        }
+
+           public List<? extends SQLObject> getChildren() {
+ return parentTable.getChildrenWithoutPopulating(containingChildType);
+           }
+
+           public Class<? extends SPObject> getContainingChildType() {
+            return containingChildType;
+        }
+
+           @Override
+           public SQLObject getParent() {
+               return parentTable;
+           }
+
+        @Override
+        public boolean allowsChildren() {
+            return true;
+        }
+
+        @Override
+        public List<? extends SQLObject> getChildrenWithoutPopulating() {
+ return parentTable.getChildrenWithoutPopulating(containingChildType);
+        }
+
+        @Override
+        public String getShortDisplayName() {
+            return "Folder for " + containingChildType.getSimpleName();
+        }
+
+        @Override
+        public String toString() {
+            return getShortDisplayName();
+        }
+
+        @Override
+        protected void populateImpl() throws SQLObjectException {
+            //do nothing
+        }
+
+        @Override
+        protected boolean removeChildImpl(SPObject child) {
+ throw new IllegalStateException("Cannot remove children from a folder, " +
+                       "remove them from the table the folder is contained 
by.");
+        }
+
+ public int childPositionOffset(Class<? extends SPObject> childType) {
+            return 0;
+        }
+
+        public List<Class<? extends SPObject>> getAllowedChildTypes() {
+ List<Class<? extends SPObject>> allowedTypes = new ArrayList<Class<? extends SPObject>>();
+            allowedTypes.add(containingChildType);
+            return allowedTypes;
+        }
+
+        public List<? extends SPObject> getDependencies() {
+            return Collections.emptyList();
+        }
+
+        public void removeDependency(SPObject dependency) {
+            //do nothing
+        }
+
+       }
+
+       /**
+ * A {...@link SPListener} implementation that will fire tree events as the underlying
+        * objects change.
+        */
+       private class DBTreeSPListener implements SPListener {
+
+        public void childAdded(SPChildEvent e) {
+            if (logger.isDebugEnabled()) {
+ logger.debug("dbChildrenInserted. source="+e.getSource() //$NON-NLS-1$
+                        +" index: "+e.getIndex() //$NON-NLS-1$
+                        +" child: "+e.getChild()); //$NON-NLS-1$
+                if (e.getSource() instanceof SQLRelationship) {
+                    SQLRelationship r = (SQLRelationship) e.getSource();
+ logger.debug("dbChildrenInserted SQLObjectEvent: "+e //$NON-NLS-1$ + +"; pk path="+Arrays.asList(getPkPathToRelationship(r)) //$NON-NLS-1$ + +"; fk path="+Arrays.asList(getFkPathToRelationship(r))); //$NON-NLS-1$
+                } else {
+ logger.debug("dbChildrenInserted SQLObjectEvent: "+e //$NON-NLS-1$ + +"; tree path="+Arrays.asList(getPathToNode(e.getSource()))); //$NON-NLS-1$
+                }
+            }
+            SQLPowerUtils.listenToHierarchy(e.getChild(), this);
+
+            Set<TreeModelEvent> events = createTreeEvents(e);
+            for (TreeModelEvent evt : events) {
+                fireTreeNodesInserted(evt);
+            }
+
+ if (e.getChild() instanceof SQLTable && foldersInTables.get(e.getChild()) == null) {
+                SQLTable table = (SQLTable) e.getChild();
+                List<FolderNode> folderList = new ArrayList<FolderNode>();
+                foldersInTables.put(table, folderList);
+ FolderNode SQLColumnFolder = new FolderNode(table, SQLColumn.class);
+                folderList.add(SQLColumnFolder);
+ FolderNode SQLRelationshipFolder = new FolderNode(table, SQLRelationship.class);
+                folderList.add(SQLRelationshipFolder);
+ FolderNode SQLIndexFolder = new FolderNode(table, SQLIndex.class);
+                folderList.add(SQLIndexFolder);
+ fireTreeNodesInserted(new TreeModelEvent(table, getPathToNode(table), + new int[]{0, 1, 2}, new Object[]{SQLColumnFolder, SQLRelationshipFolder, SQLIndexFolder}));
+            }
+
+        }
+
+        public void childRemoved(SPChildEvent e) {
+            if (logger.isDebugEnabled()) {
+ logger.debug("dbchildrenremoved. source="+e.getSource() //$NON-NLS-1$
+                        +" index: "+e.getIndex() //$NON-NLS-1$
+                        +" child: "+e.getChild()); //$NON-NLS-1$
+            }
+ if (logger.isDebugEnabled()) logger.debug("dbChildrenRemoved SQLObjectEvent: "+e); //$NON-NLS-1$
+            SQLPowerUtils.unlistenToHierarchy(e.getChild(), this);
+            if (e.getChild() instanceof SQLTable) {
+                foldersInTables.remove(e.getChild());
+            }
+
+            Set<TreeModelEvent> events = createTreeEvents(e);
+            for (TreeModelEvent evt : events) {
+                fireTreeNodesRemoved(evt);
+            }
+        }
+
+        public void transactionEnded(TransactionEvent e) {
+            //do nothing
+        }
+
+        public void transactionRollback(TransactionEvent e) {
+            //do nothing
+        }
+
+        public void transactionStarted(TransactionEvent e) {
+            //do nothing
+        }
+
+        public void propertyChange(PropertyChangeEvent e) {
+ logger.debug("dbObjectChanged. source="+e.getSource()); //$NON-NLS-1$ + if ((!SwingUtilities.isEventDispatchThread()) && (!refireOnAnyThread)) { + logger.debug("Not refiring because this is not the EDT. You will need to call refreshTreeStructure() at some point in the future."); //$NON-NLS-1$
+                return;
+            }
+ if (logger.isDebugEnabled()) logger.debug("dbObjectChanged SQLObjectEvent: "+e); //$NON-NLS-1$
+            processSQLObjectChanged(e);
+        }
+
+        /**
+         * Creates a TreeModelEvent for a SPChildEvent.
+         */
+        private Set<TreeModelEvent> createTreeEvents(SPChildEvent change) {
+            Set<TreeModelEvent> events = new HashSet<TreeModelEvent>();
+            SPObject parent = change.getSource();
+            SPObject child = change.getChild();
+            if (parent instanceof SQLRelationship) {
+ // SQLRelationships are in the tree twice, must fire two events + events.add(new TreeModelEvent(DBTreeModel.this, getPkPathToRelationship((SQLRelationship) parent), new int[]{change.getIndex()}, new Object[]{child})); + events.add(new TreeModelEvent(DBTreeModel.this, getFkPathToRelationship((SQLRelationship) parent), new int[]{change.getIndex()}, new Object[]{child}));
+            } else {
+                if (parent instanceof SQLTable) {
+                    for (FolderNode folder : foldersInTables.get(parent)) {
+ if (folder.getContainingChildType().isAssignableFrom(child.getClass())) {
+                            parent = folder;
+                            break;
+                        }
+                    }
+                }
+ events.add(new TreeModelEvent(DBTreeModel.this, getPathToNode(parent), new int[]{change.getIndex()}, new Object[]{child}));
+            }
+            return events;
+        }
+
+        /**
+         * The profile manager needs to fire events from different threads.
+         * So we need to get around the check.
+         */
+        private void processSQLObjectChanged(PropertyChangeEvent e) {
+            if (e.getPropertyName().equals("name") &&  //$NON-NLS-1$
+ !e.getNewValue().equals(((SPObject) e.getSource()).getName()) ) { + logger.error("Name change event has wrong new value. new="+e.getNewValue()+"; real="+((SPObject) e.getSource()).getName()); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            SPObject source = (SPObject) e.getSource();
+            if (source instanceof SQLRelationship) {
+                SQLRelationship r = (SQLRelationship) source;
+ fireTreeNodesChanged(new TreeModelEvent(this, getPkPathToRelationship(r))); + fireTreeNodesChanged(new TreeModelEvent(this, getFkPathToRelationship(r)));
+            } else {
+ fireTreeNodesChanged(new TreeModelEvent(this, getPathToNode(source)));
+            }
+        }
+
+       }
+
+       private final DBTreeSPListener treeListener = new DBTreeSPListener();
+
     /**
      * When this flag is true, the DBTreeModel's protection against firing
* TreeModelEvents on the wrong thread are disabled. The only legitimate
@@ -63,15 +289,7 @@

        protected SQLObject root;

-       /**
- * Map of parents to children and indices of nodes inserted during the transaction
-        */
- private Map<SPObject, HashMap<Integer, SPObject>> insertedNodes = new HashMap<SPObject, HashMap<Integer, SPObject>>();
-
-       /**
- * Map of parents to children and indices of nodes removed during the transaction
-        */
- private Map<SPObject, HashMap<Integer, SPObject>> removedNodes = new HashMap<SPObject, HashMap<Integer, SPObject>>(); + protected final Map<SQLTable, List<FolderNode>> foldersInTables = new HashMap<SQLTable, List<FolderNode>>();

        /**
         * Creates a tree model with all of the SQLDatabase objects in the
@@ -85,7 +303,7 @@
        public DBTreeModel(SQLObjectRoot root) throws SQLObjectException {
                this.root = root;
                this.treeModelListeners = new LinkedList();
-               SQLPowerUtils.listenToHierarchy(root, this);
+               SQLPowerUtils.listenToHierarchy(root, treeListener);
        }

        public Object getRoot() {
@@ -95,6 +313,13 @@

        public Object getChild(Object parent, int index) {
if (logger.isDebugEnabled()) logger.debug("DBTreeModel.getChild("+parent+","+index+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+               if (parent instanceof FolderNode) {
+                   return ((FolderNode) parent).getChildren().get(index);
+               } else if (parent instanceof SQLTable) {
+                   return foldersInTables.get((SQLTable) parent).get(index);
+               }
+
                SQLObject sqlParent = (SQLObject) parent;
                try {
if (logger.isDebugEnabled()) logger.debug("returning "+sqlParent.getChild(index)); //$NON-NLS-1$
@@ -106,6 +331,13 @@

        public int getChildCount(Object parent) {
if (logger.isDebugEnabled()) logger.debug("DBTreeModel.getChildCount("+parent+")"); //$NON-NLS-1$ //$NON-NLS-2$
+
+               if (parent instanceof FolderNode) {
+            return ((FolderNode) parent).getChildren().size();
+        } else if (parent instanceof SQLTable) {
+            return foldersInTables.get((SQLTable) parent).size();
+        }
+
                SQLObject sqlParent = (SQLObject) parent;
                try {
if (logger.isDebugEnabled()) logger.debug("returning "+sqlParent.getChildCount()); //$NON-NLS-1$
@@ -117,6 +349,9 @@

        public boolean isLeaf(Object parent) {
if (logger.isDebugEnabled()) logger.debug("DBTreeModel.isLeaf("+parent+"): returning "+!((SQLObject) parent).allowsChildren()); //$NON-NLS-1$ //$NON-NLS-2$
+               if (parent instanceof FolderNode) {
+                   return false;
+               }
                return !((SQLObject) parent).allowsChildren();
        }

@@ -126,6 +361,13 @@

        public int getIndexOfChild(Object parent, Object child) {
if (logger.isDebugEnabled()) logger.debug("DBTreeModel.getIndexOfChild("+parent+","+child+"): returning "+((SQLObject) parent).getChildren().indexOf(child)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+               if (parent instanceof FolderNode) {
+            return ((FolderNode) parent).getChildren().indexOf(child);
+        } else if (parent instanceof SQLTable) {
+            return foldersInTables.get((SQLTable) parent).indexOf(child);
+        }
+
         return ((SQLObject) parent).getChildren().indexOf(child);
        }

@@ -232,6 +474,14 @@
                }
                LinkedList path = new LinkedList();
                while (node != null && node != root) {
+                   if (path.size() > 0 && node instanceof SQLTable) {
+                       for (FolderNode folder : foldersInTables.get(node)) {
+ if (folder.getContainingChildType().isAssignableFrom(path.get(0).getClass())) {
+                               path.add(0, folder);
+                               break;
+                           }
+                       }
+                   }
                        path.add(0, node);
                        node = node.getParent();
                }
@@ -274,128 +524,6 @@
            }
            return nodePaths;
        }
-
-
- // --------------------- SQLObject listener support -----------------------
-       @Override
-       public void childAddedImpl(SPChildEvent e) {
-        if (logger.isDebugEnabled()) {
- logger.debug("dbChildrenInserted. source="+e.getSource() //$NON-NLS-1$
-                    +" index: "+e.getIndex() //$NON-NLS-1$
-                    +" child: "+e.getChild()); //$NON-NLS-1$
-                       if (e.getSource() instanceof SQLRelationship) {
-                               SQLRelationship r = (SQLRelationship) 
e.getSource();
-                               logger.debug("dbChildrenInserted SQLObjectEvent: 
"+e //$NON-NLS-1$
- +"; pk path="+Arrays.asList(getPkPathToRelationship(r)) //$NON-NLS-1$ - +"; fk path="+Arrays.asList(getFkPathToRelationship(r))); //$NON-NLS-1$
-                       } else {
-                               logger.debug("dbChildrenInserted SQLObjectEvent: 
"+e //$NON-NLS-1$
- +"; tree path="+Arrays.asList(getPathToNode(e.getSource()))); //$NON-NLS-1$
-                       }
-               }
-        SQLPowerUtils.listenToHierarchy(e.getChild(), this);
-
-        if (!insertedNodes.containsKey(e.getSource())) {
- insertedNodes.put(e.getSource(), new HashMap<Integer, SPObject>());
-        }
-        insertedNodes.get(e.getSource()).put(e.getIndex(), e.getChild());
-       }
-
-       @Override
-       public void childRemovedImpl(SPChildEvent e) {
-        if (logger.isDebugEnabled()) {
- logger.debug("dbchildrenremoved. source="+e.getSource() //$NON-NLS-1$
-                    +" index: "+e.getIndex() //$NON-NLS-1$
-                    +" child: "+e.getChild()); //$NON-NLS-1$
-        }
- if (logger.isDebugEnabled()) logger.debug("dbChildrenRemoved SQLObjectEvent: "+e); //$NON-NLS-1$
-               SQLPowerUtils.unlistenToHierarchy(e.getChild(), this);
-
-               if (!removedNodes.containsKey(e.getSource())) {
-                   removedNodes.put(e.getSource(), new HashMap<Integer, 
SPObject>());
-               }
-               removedNodes.get(e.getSource()).put(e.getIndex(), e.getChild());
-       }
-
-       @Override
-       public void propertyChangeImpl(PropertyChangeEvent e) {
-               logger.debug("dbObjectChanged. source="+e.getSource()); 
//$NON-NLS-1$
- if ((!SwingUtilities.isEventDispatchThread()) && (!refireOnAnyThread)) { - logger.debug("Not refiring because this is not the EDT. You will need to call refreshTreeStructure() at some point in the future."); //$NON-NLS-1$
-            return;
-        }
- if (logger.isDebugEnabled()) logger.debug("dbObjectChanged SQLObjectEvent: "+e); //$NON-NLS-1$
-               processSQLObjectChanged(e);
-       }
-
-       @Override
-       protected void finalCommitImpl(TransactionEvent e) {
- if ((!SwingUtilities.isEventDispatchThread()) && (!refireOnAnyThread)) { - logger.debug("Not refiring because this is not the EDT. You will need to call refreshTreeStructure() at some point in the future."); //$NON-NLS-1$
-            return;
-        }
-
-           for (TreeModelEvent insert : createTreeEvents(insertedNodes)) {
-               fireTreeNodesInserted(insert);
-           }
-           insertedNodes.clear();
-
-           for (TreeModelEvent remove : createTreeEvents(removedNodes)) {
-            fireTreeNodesRemoved(remove);
-        }
-           removedNodes.clear();
-       }
-
-       /**
- * Creates a set of TreeModelEvents, one for each parent, based on the given map.
-        */
- private Set<TreeModelEvent> createTreeEvents(Map<SPObject, HashMap<Integer, SPObject>> changes) {
-           Set<TreeModelEvent> events = new HashSet<TreeModelEvent>();
- for (Map.Entry<SPObject, HashMap<Integer, SPObject>> e : changes.entrySet()) {
-               if (e.getKey() instanceof SQLRelationship) {
- // SQLRelationships are in the tree twice, must fire two events - events.add(createTreeModelEvent(getPkPathToRelationship((SQLRelationship) e.getKey()), e.getValue())); - events.add(createTreeModelEvent(getFkPathToRelationship((SQLRelationship) e.getKey()), e.getValue()));
-               } else {
- events.add(createTreeModelEvent(getPathToNode(e.getKey()), e.getValue()));
-               }
-           }
-           return events;
-       }
-
-       /**
-        * Creates a single TreeModelEvent for the given path and Map of 
children.
-        */
- private TreeModelEvent createTreeModelEvent(SPObject[] path, HashMap<Integer, SPObject> changes) {
-           int[] indexes = new int[changes.size()];
-           SPObject[] children = new SPObject[changes.size()];
- Iterator<Map.Entry <Integer, SPObject>> it = changes.entrySet().iterator();
-           for (int i = 0; it.hasNext(); i++) {
-               Map.Entry<Integer, SPObject> e = it.next();
-               indexes[i] = e.getKey();
-               children[i] = e.getValue();
-           }
-           return new TreeModelEvent(this, path, indexes, children);
-       }
-
-    /**
-     * The profile manager needs to fire events from different threads.
-     * So we need to get around the check.
-     */
-    private void processSQLObjectChanged(PropertyChangeEvent e) {
-        if (e.getPropertyName().equals("name") &&  //$NON-NLS-1$
-                               !e.getNewValue().equals(((SPObject) 
e.getSource()).getName()) ) {
- logger.error("Name change event has wrong new value. new="+e.getNewValue()+"; real="+((SPObject) e.getSource()).getName()); //$NON-NLS-1$ //$NON-NLS-2$
-               }
-               SPObject source = (SPObject) e.getSource();
-               if (source instanceof SQLRelationship) {
-                       SQLRelationship r = (SQLRelationship) source;
- fireTreeNodesChanged(new TreeModelEvent(this, getPkPathToRelationship(r))); - fireTreeNodesChanged(new TreeModelEvent(this, getFkPathToRelationship(r)));
-               } else {
-                       fireTreeNodesChanged(new TreeModelEvent(this, 
getPathToNode(source)));
-               }
-    }

     /**
      * When this flag is true, the DBTreeModel's protection against firing

Reply via email to