Author: thomasobrien95
Date: Thu Dec 11 07:42:05 2008
New Revision: 2871

Added:
trunk/src/ca/sqlpower/architect/swingui/dbtree/icons/stop16.png (contents, props changed)
Removed:
   trunk/regress/ca/sqlpower/architect/TestSQLExceptionNode.java
   trunk/src/ca/sqlpower/architect/SQLExceptionNode.java
Modified:
   trunk/regress/ca/sqlpower/architect/ArchitectValueMaker.java
   trunk/regress/ca/sqlpower/architect/SQLObjectTest.java
   trunk/regress/ca/sqlpower/architect/SQLTestCase.java
   trunk/regress/ca/sqlpower/architect/StubSQLObject.java
   trunk/regress/ca/sqlpower/architect/swingui/TestSwingUIProject.java
   trunk/src/ca/sqlpower/architect/CoreProject.java
   trunk/src/ca/sqlpower/architect/SQLCatalog.java
   trunk/src/ca/sqlpower/architect/SQLColumn.java
   trunk/src/ca/sqlpower/architect/SQLDatabase.java
   trunk/src/ca/sqlpower/architect/SQLIndex.java
   trunk/src/ca/sqlpower/architect/SQLObject.java
   trunk/src/ca/sqlpower/architect/SQLObjectRoot.java
   trunk/src/ca/sqlpower/architect/SQLRelationship.java
   trunk/src/ca/sqlpower/architect/SQLSchema.java
   trunk/src/ca/sqlpower/architect/SQLSequence.java
   trunk/src/ca/sqlpower/architect/SQLTable.java
   trunk/src/ca/sqlpower/architect/swingui/DBTree.java
   trunk/src/ca/sqlpower/architect/swingui/PlayPen.java
   trunk/src/ca/sqlpower/architect/swingui/SwingUIProject.java
   trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeCellRenderer.java
   trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java

Log:
Fix for bug 1181. The SQLExceptionNode has been removed and a childrenInaccessibleReason property now exists on SQLObjects to give the exception for why the object cannot populate its children. This solves the problem where populating a SQLObject partially then trying to add an exception node would throw an exception that child types cannot be mixed.

Modified: trunk/regress/ca/sqlpower/architect/ArchitectValueMaker.java
==============================================================================
--- trunk/regress/ca/sqlpower/architect/ArchitectValueMaker.java        
(original)
+++ trunk/regress/ca/sqlpower/architect/ArchitectValueMaker.java Thu Dec 11 07:42:05 2008
@@ -38,6 +38,8 @@
             newVal = new AlwaysOKUserPrompter();
         } else if (valueType == KettleRepositoryDirectoryChooser.class) {
             newVal = new RootRepositoryDirectoryChooser();
+        } else if (valueType.isAssignableFrom(Throwable.class)) {
+            newVal = new ArchitectException("Test Exception");
         } else {
             newVal = super.makeNewValue(valueType, oldVal, propName);
         }

Modified: trunk/regress/ca/sqlpower/architect/SQLObjectTest.java
==============================================================================
--- trunk/regress/ca/sqlpower/architect/SQLObjectTest.java      (original)
+++ trunk/regress/ca/sqlpower/architect/SQLObjectTest.java Thu Dec 11 07:42:05 2008
@@ -47,7 +47,7 @@
                        this.parent = parent;
                }
                @Override
-               protected void populate() throws ArchitectException {
+               protected void populateImpl() throws ArchitectException {
                        // System.err.println("Abstract test stub populate() 
invoked");
                }
                @Override
@@ -199,7 +199,7 @@
     }
        
        public void testNoMixChildTypes() throws ArchitectException {
- target.addChild(new SQLExceptionNode(null, "everything is ok. don't panic."));
+               target.addChild(new SQLColumn());
                try {
                        target.addChild(new SQLObjectImpl());
                        fail("Target didn't throw exception for mixing child 
types!");
@@ -258,6 +258,26 @@
         target.addSQLObjectListener(listener);
target.putClientProperty(this.getClass(), "testProperty", "test me");
         assertEquals(1, listener.getChangedCount());
+    }
+
+ public void testChildrenInaccessibleReasonSetOnPopulateError() throws Exception {
+        final RuntimeException e = new RuntimeException();
+        SQLObject o = new SQLObjectImpl() {
+            @Override
+            protected void populateImpl() throws ArchitectException {
+                throw e;
+            }
+        };
+
+        try {
+            o.populate();
+            fail();
+        } catch (Exception ex) {
+            //should get here
+        }
+
+        assertEquals(e, o.getChildrenInaccessibleReason());
+
     }

 }

Modified: trunk/regress/ca/sqlpower/architect/SQLTestCase.java
==============================================================================
--- trunk/regress/ca/sqlpower/architect/SQLTestCase.java        (original)
+++ trunk/regress/ca/sqlpower/architect/SQLTestCase.java Thu Dec 11 07:42:05 2008
@@ -177,6 +177,8 @@
                 } else {
                     newVal = SQLRelationship.UpdateDeleteRule.CASCADE;
                 }
+            } else if (property.getPropertyType() == Throwable.class) {
+                newVal = new Throwable();
             } else {
                                throw new RuntimeException("This test case lacks a 
value for "+
                                                property.getName()+
@@ -296,7 +298,8 @@
                 } else {
newVal = SQLRelationship.Deferrability.INITIALLY_DEFERRED;
                 }
-
+            } else if (property.getPropertyType() == Throwable.class) {
+                newVal = new Throwable();
             } else {
                                throw new RuntimeException("This test case lacks a 
value for "+
                                                property.getName()+

Modified: trunk/regress/ca/sqlpower/architect/StubSQLObject.java
==============================================================================
--- trunk/regress/ca/sqlpower/architect/StubSQLObject.java      (original)
+++ trunk/regress/ca/sqlpower/architect/StubSQLObject.java Thu Dec 11 07:42:05 2008
@@ -47,7 +47,7 @@
     }

     @Override
-    protected void populate() throws ArchitectException {
+    protected void populateImpl() throws ArchitectException {
         populateCount++;
     }


Modified: trunk/regress/ca/sqlpower/architect/swingui/TestSwingUIProject.java
==============================================================================
--- trunk/regress/ca/sqlpower/architect/swingui/TestSwingUIProject.java (original) +++ trunk/regress/ca/sqlpower/architect/swingui/TestSwingUIProject.java Thu Dec 11 07:42:05 2008
@@ -410,9 +410,11 @@
                
                Map<String,Object> oldDescription =
                        TestUtils.setAllInterestingProperties(db, 
propertiesToIgnore);
+               System.out.println("Properties set " + oldDescription);
                
                
                File tmp = File.createTempFile("test", ".architect");
+ System.out.println("File located at " + tmp.getAbsolutePath() + " and is delete on exit? " + deleteOnExit);
                if (deleteOnExit) {
                        tmp.deleteOnExit();
                }
@@ -428,6 +430,8 @@
// grab the second database in the dbtree's model (the first is the play pen) db = (SQLDatabase) session2.getSourceDatabases().getDatabaseList().get(1);
                
+ System.out.println("DB has child exception " + db.getChildrenInaccessibleReason());
+               
                Map<String, Object> newDescription =
                        TestUtils.getAllInterestingProperties(db, 
propertiesToIgnore);
                
@@ -527,6 +531,7 @@
                propertiesToIgnore.add("populated");
                propertiesToIgnore.add("secondaryChangeMode");
         propertiesToIgnore.add("magicEnabled");
+        propertiesToIgnore.add("childrenInaccessibleReason");

                Map<String,Object> oldDescription =
                        TestUtils.setAllInterestingProperties(target, 
propertiesToIgnore);
@@ -581,6 +586,7 @@
                propertiesToIgnore.add("columnsFolder");
                propertiesToIgnore.add("secondaryChangeMode");
         propertiesToIgnore.add("magicEnabled");
+        propertiesToIgnore.add("childrenInaccessibleReason");

                Map<String,Object> oldDescription =
                        TestUtils.setAllInterestingProperties(target, 
propertiesToIgnore);
@@ -632,6 +638,7 @@
                propertiesToIgnore.add("undoEventListeners");
                propertiesToIgnore.add("secondaryChangeMode");
         propertiesToIgnore.add("magicEnabled");
+        propertiesToIgnore.add("childrenInaccessibleReason");

                Map<String,Object> oldDescription =
                        TestUtils.setAllInterestingProperties(target, 
propertiesToIgnore);

Modified: trunk/src/ca/sqlpower/architect/CoreProject.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/CoreProject.java    (original)
+++ trunk/src/ca/sqlpower/architect/CoreProject.java Thu Dec 11 07:42:05 2008
@@ -68,6 +68,17 @@
ConvertUtils.register(new UpdateDeleteRuleConverter(), UpdateDeleteRule.class);
     }

+    /**
+ * This will load the attributes in all SQLObjects that are not loaded by basic
+     * setters through the digester.
+     */
+ private static void LoadSQLObjectAttributes(SQLObject obj, Attributes attr) {
+        String message = attr.getValue("sql-exception");
+        if (message != null) {
+ obj.setChildrenInaccessibleReason(new ArchitectException(message));
+        }
+    }
+
 //  ---------------- persistent properties -------------------

     protected File file;
@@ -369,7 +380,7 @@
         SQLExceptionFactory exceptionFactory = new SQLExceptionFactory();
         d.addFactoryCreate("*/sql-exception", exceptionFactory);
         d.addSetProperties("*/sql-exception");
-        d.addSetNext("*/sql-exception", "addChild");
+        d.addSetNext("*/sql-exception", "setChildrenInaccessibleReason");

         TargetDBFactory targetDBFactory = new TargetDBFactory();
         // target database hierarchy
@@ -491,6 +502,8 @@
             if (populated != null && populated.equals("false")) {
                 db.setPopulated(false);
             }
+
+            LoadSQLObjectAttributes(db, attributes);

             return db;
         }
@@ -512,6 +525,8 @@
             } else {
logger.warn("No ID found in database element while loading project!");
             }
+
+            LoadSQLObjectAttributes(schema, attributes);

             return schema;
         }
@@ -552,6 +567,9 @@
             }

             currentTable = tab;
+
+            LoadSQLObjectAttributes(tab, attributes);
+
             return tab;
         }
     }
@@ -588,6 +606,9 @@
             } catch (ArchitectException ex) {
                 throw new ArchitectRuntimeException(ex);
             }
+
+            LoadSQLObjectAttributes(f, attributes);
+
             return f;
         }
     }
@@ -612,6 +633,8 @@
             if (sourceId != null) {
col.setSourceColumn((SQLColumn) sqlObjectLoadIdMap.get(sourceId));
             }
+
+            LoadSQLObjectAttributes(col, attributes);

             return col;
         }
@@ -619,22 +642,12 @@

     /**
      * Creates a SQLException instance and adds it to the
-     * objectIdMap.
+     * objectIdMap. This ExceptionFactory is still used for loading older
+     * files.
      */
private class SQLExceptionFactory extends AbstractObjectCreationFactory {
         public Object createObject(Attributes attributes) {
-            SQLExceptionNode exc = new SQLExceptionNode(null, null);
-
-            String id = attributes.getValue("id");
-            if (id != null) {
-                sqlObjectLoadIdMap.put(id, exc);
-            } else {
- logger.warn("No ID found in exception element while loading project!");
-            }
-
-            exc.setMessage(attributes.getValue("message"));
-
-            return exc;
+            return new Exception(attributes.getValue("message"));
         }
     }

@@ -670,6 +683,8 @@
JOptionPane.showMessageDialog(null, "Missing pktable or fktable references for relationship id \""+id+"\"");
             }

+            LoadSQLObjectAttributes(rel, attributes);
+
             return rel;
         }
     }
@@ -730,6 +745,9 @@
             index.setType(attributes.getValue("index-type"));

             currentIndex = index;
+
+            LoadSQLObjectAttributes(index, attributes);
+
             return index;
         }
     }
@@ -761,6 +779,8 @@
             if (attributes.getValue("ascendingOrDescending") != null) {
col.setAscendingOrDescending(SQLIndex.AscendDescend.valueOf(attributes.getValue("ascendingOrDescending")));
             }
+
+            LoadSQLObjectAttributes(col, attributes);

             return col;
         }

Modified: trunk/src/ca/sqlpower/architect/SQLCatalog.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLCatalog.java     (original)
+++ trunk/src/ca/sqlpower/architect/SQLCatalog.java     Thu Dec 11 07:42:05 2008
@@ -122,7 +122,7 @@
                return true;
        }

-       public void populate() throws ArchitectException {
+       public void populateImpl() throws ArchitectException {
                if (populated) return;

                logger.debug("SQLCatalog: populate starting");

Modified: trunk/src/ca/sqlpower/architect/SQLColumn.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLColumn.java      (original)
+++ trunk/src/ca/sqlpower/architect/SQLColumn.java      Thu Dec 11 07:42:05 2008
@@ -404,7 +404,7 @@

        // ------------------------- SQLObject support -------------------------

-       public void populate() throws ArchitectException {
+       public void populateImpl() throws ArchitectException {
                // SQLColumn: populate is a no-op
        }


Modified: trunk/src/ca/sqlpower/architect/SQLDatabase.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLDatabase.java    (original)
+++ trunk/src/ca/sqlpower/architect/SQLDatabase.java Thu Dec 11 07:42:05 2008
@@ -87,7 +87,8 @@
                return connectionPool != null;
        }

-       public synchronized void populate() throws ArchitectException {
+       public synchronized void populateImpl() throws ArchitectException {
+           logger.debug("SQLDatabase: is populated " + populated);
                if (populated) return;
                int oldSize = children.size();
                
@@ -545,8 +546,10 @@
                                    getConnectionPool().getNumActive() + 1);
                                return (Connection) 
getConnectionPool().borrowObject();
                        } catch (Exception e) {
-                               throw new ArchitectException(
-                                               "Couldn't connect to database: 
"+e.getMessage(), e);
+                           ArchitectException ex = new ArchitectException(
+                                   "Couldn't connect to database: 
"+e.getMessage(), e);
+                           setChildrenInaccessibleReason(ex);
+                           throw ex;
                        }
                }
        }

Modified: trunk/src/ca/sqlpower/architect/SQLIndex.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLIndex.java       (original)
+++ trunk/src/ca/sqlpower/architect/SQLIndex.java       Thu Dec 11 07:42:05 2008
@@ -185,7 +185,7 @@
         }

         @Override
-        protected void populate() throws ArchitectException {
+        protected void populateImpl() throws ArchitectException {
             // nothing to do
         }

@@ -448,7 +448,7 @@
      * Indices are populated when first created, so populate is a no-op.
      */
     @Override
-    protected void populate() throws ArchitectException {
+    protected void populateImpl() throws ArchitectException {
         // nothing to do
     }

@@ -800,6 +800,7 @@
         index.setPrimaryKeyIndex(source.isPrimaryKeyIndex());
         index.setPhysicalName(source.getPhysicalName());
         index.setClustered(source.isClustered());
+ index.setChildrenInaccessibleReason(source.getChildrenInaccessibleReason());

         for (Column column : source.getChildren()) {
             Column newColumn;

Modified: trunk/src/ca/sqlpower/architect/SQLObject.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLObject.java      (original)
+++ trunk/src/ca/sqlpower/architect/SQLObject.java      Thu Dec 11 07:42:05 2008
@@ -98,6 +98,13 @@
* When this counter is > 0, the fireXXX methods will ignore secondary changes.
         */
        protected int magicDisableCount = 0;
+       
+       /**
+ * This is the throwable that tells if the children of this component can be reached + * or not. If this is null then the children can be reached. If it is not null + * then there was an exception the last time the children were attempted to be accessed.
+        */
+       private Throwable childrenInaccessibleReason = null;
        
        public synchronized void setMagicEnabled(boolean enable) {
                if (magicDisableCount < 0) {
@@ -181,6 +188,24 @@
         * during addChild and removeChild requests.
         */
        protected abstract void setParent(SQLObject parent);
+       
+       /**
+ * Causes this SQLObject to load its children through populateImpl (if any exist).
+     * This will do nothing if the object is already populated.
+     */
+       public void populate() throws ArchitectException {
+           if (populated) return;
+           childrenInaccessibleReason = null;
+           try {
+               populateImpl();
+           } catch (ArchitectException e) {
+               childrenInaccessibleReason = e;
+               throw e;
+           } catch (RuntimeException e) {
+               childrenInaccessibleReason = e;
+               throw e;
+           }
+       }

        /**
         * Causes this SQLObject to load its children (if any exist).
@@ -188,7 +213,7 @@
         * not you need to do anything and return right away whenever
         * possible.
         */
-       protected abstract void populate() throws ArchitectException;
+       protected abstract void populateImpl() throws ArchitectException;

        

@@ -263,22 +288,10 @@
                                ! 
(children.get(0).getClass().isAssignableFrom(newChild.getClass())
|| newChild.getClass().isAssignableFrom(children.get(0).getClass()))) {

-            ArchitectException ex;
-            if (newChild instanceof SQLExceptionNode) {
-
- // long term, we want to dispose of SQLExceptionNode altogether. This is a temporary workaround.
-                SQLExceptionNode sen = (SQLExceptionNode) newChild;
-                ex = new ArchitectException(
- "Can't add exception node here because there are already other children. " +
-                        "See exception cause for the original exception.",
-                        sen.getException());
-            } else {
-                ex = new ArchitectException(
-                        "You Can't mix SQL Object Types! You gave: " +
-                        newChild.getClass().getName() +
-                        "; I need " + children.get(0).getClass());
-            }
-                       throw ex;
+            throw new ArchitectException(
+                    "You Can't mix SQL Object Types! You gave: " +
+                    newChild.getClass().getName() +
+                    "; I need " + children.get(0).getClass());
                }
                children.add(index, newChild);
                newChild.setParent(this);
@@ -734,5 +747,20 @@
      */
     public Set<String> getClientPropertyNames() {
         return clientProperties.keySet();
+    }
+
+    public Throwable getChildrenInaccessibleReason() {
+        return childrenInaccessibleReason;
+    }
+
+    /**
+ * This setter will take in either a Throwable to set the inaccessible reason + * to, for things like copy methods, or a string of the exception message, for
+     * things like loading the exception.
+     */
+    public void setChildrenInaccessibleReason(Throwable message) {
+        Throwable oldVal = this.childrenInaccessibleReason;
+        this.childrenInaccessibleReason = message;
+ fireDbObjectChanged("childrenInaccessibleReason", oldVal, childrenInaccessibleReason);
     }
 }

Modified: trunk/src/ca/sqlpower/architect/SQLObjectRoot.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLObjectRoot.java  (original)
+++ trunk/src/ca/sqlpower/architect/SQLObjectRoot.java Thu Dec 11 07:42:05 2008
@@ -32,7 +32,7 @@
                return true;
        }
        
-       public void populate() throws ArchitectException {
+       public void populateImpl() throws ArchitectException {
                return;
        }
        

Modified: trunk/src/ca/sqlpower/architect/SQLRelationship.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLRelationship.java        (original)
+++ trunk/src/ca/sqlpower/architect/SQLRelationship.java Thu Dec 11 07:42:05 2008
@@ -286,6 +286,7 @@
         setUpdateRule(relationshipToCopy.getUpdateRule());
         setDeleteRule(relationshipToCopy.getDeleteRule());
         setDeferrability(relationshipToCopy.getDeferrability());
+ setChildrenInaccessibleReason(relationshipToCopy.getChildrenInaccessibleReason());
        }
        
        /**
@@ -645,7 +646,11 @@
         * <p>XXX: should be removed when SQLObject API gets generics
         */
        public List<ColumnMapping> getMappings() {
-               populate(); // doesn't do anything yet, but better safe than 
sorry
+           try {
+               populate(); // doesn't do anything yet, but better safe than 
sorry
+           } catch (ArchitectException e) {
+               throw new RuntimeException(e);
+           }
                return Collections.unmodifiableList(children);
        }

@@ -1030,7 +1035,7 @@
        /**
         * This class is not a lazy-loading class.  This call does nothing.
         */
-       public void populate() {
+       public void populateImpl() {
                // nothing to do.
        }

@@ -1282,7 +1287,7 @@
                /**
                 * This class is not a lazy-loading class.  This call does 
nothing.
                 */
-               public void populate() throws ArchitectException {
+               public void populateImpl() throws ArchitectException {
                        return;
                }


Modified: trunk/src/ca/sqlpower/architect/SQLSchema.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLSchema.java      (original)
+++ trunk/src/ca/sqlpower/architect/SQLSchema.java      Thu Dec 11 07:42:05 2008
@@ -99,7 +99,7 @@
         *
         * @throws NullPointerException if this schema has no parent database.
         */
-       public void populate() throws ArchitectException {
+       public void populateImpl() throws ArchitectException {
                if (populated) return;
                
                logger.debug("SQLSchema: populate starting");

Modified: trunk/src/ca/sqlpower/architect/SQLSequence.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLSequence.java    (original)
+++ trunk/src/ca/sqlpower/architect/SQLSequence.java Thu Dec 11 07:42:05 2008
@@ -88,7 +88,7 @@
      * Does nothing because this type of object is not reverse-engineered.
      */
     @Override
-    protected void populate() throws ArchitectException {
+    protected void populateImpl() throws ArchitectException {
         // no op
     }


Modified: trunk/src/ca/sqlpower/architect/SQLTable.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/SQLTable.java       (original)
+++ trunk/src/ca/sqlpower/architect/SQLTable.java       Thu Dec 11 07:42:05 2008
@@ -155,6 +155,7 @@
                SQLTable t = new SQLTable(parent, true);
                t.setName(source.getName());
                t.remarks = source.remarks;
+               
t.setChildrenInaccessibleReason(source.getChildrenInaccessibleReason());

                t.setPhysicalName(source.getPhysicalName());
                t.physicalPrimaryKeyName = source.getPhysicalPrimaryKeyName();
@@ -842,7 +843,7 @@
* step. The various populate operations (columns, keys, indices) are triggered
      * by visiting the individual folders.
         */
-       public void populate() throws ArchitectException {
+       public void populateImpl() throws ArchitectException {
                // SQLTable: populate is a no-op
        }

@@ -915,7 +916,7 @@
                        parent = (SQLTable) newParentTable;
                }

-               public void populate() throws ArchitectException {
+               public void populateImpl() throws ArchitectException {
                    populate(null);
                }
                

Modified: trunk/src/ca/sqlpower/architect/swingui/DBTree.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/DBTree.java (original)
+++ trunk/src/ca/sqlpower/architect/swingui/DBTree.java Thu Dec 11 07:42:05 2008
@@ -33,6 +33,7 @@
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -58,7 +59,6 @@
 import ca.sqlpower.architect.SQLCatalog;
 import ca.sqlpower.architect.SQLColumn;
 import ca.sqlpower.architect.SQLDatabase;
-import ca.sqlpower.architect.SQLExceptionNode;
 import ca.sqlpower.architect.SQLIndex;
 import ca.sqlpower.architect.SQLObject;
 import ca.sqlpower.architect.SQLRelationship;
@@ -109,7 +109,7 @@

        // ----------- CONSTRUCTORS ------------

-       public DBTree(ArchitectSwingSession session) throws ArchitectException {
+ public DBTree(final ArchitectSwingSession session) throws ArchitectException {
         this.session = session;
         setModel(new DBTreeModel(session.getRootObject()));
                setUI(new MultiDragTreeUI());
@@ -131,6 +131,29 @@
         tcr.addIconFilter(new ProfiledTableIconFilter());
         setCellRenderer(tcr);
         selectAllChildTablesAction = new SelectAllChildTablesAction();
+        addMouseListener(new MouseListener() {
+            public void mouseReleased(MouseEvent e) {
+                if (getPathForLocation(e.getX(), e.getY()) != null) {
+ Object node = getPathForLocation(e.getX(), e.getY()).getLastPathComponent(); + if (e.getClickCount() == 2 && node instanceof SQLObject && ((SQLObject) node).getChildrenInaccessibleReason() != null) { + SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(), + Messages.getString("DBTree.exceptionNodeReport"), ((SQLObject) node).getChildrenInaccessibleReason()); //$NON-NLS-1$
+                    }
+                }
+            }
+            public void mousePressed(MouseEvent e) {
+                //no-op
+            }
+            public void mouseExited(MouseEvent e) {
+                //no-op
+            }
+            public void mouseEntered(MouseEvent e) {
+                //no-op
+            }
+            public void mouseClicked(MouseEvent e) {
+                //no-op
+            }
+        });
        }

        // ----------- INSTANCE METHODS ------------
@@ -468,7 +491,7 @@
                                //tree only has one child
if (!tempDB.isCatalogContainer() && !tempDB.isSchemaContainer() &&
                                        (!(tempDB.getChildCount() == 1) ||
- !tempDB.getChild(0).getClass().equals(SQLExceptionNode.class)))
+                                               
tempDB.getChildrenInaccessibleReason() == null))
                                {
                                    //a new action is needed to maintain the 
database variable
CompareToCurrentAction compareToCurrentAction = new CompareToCurrentAction();
@@ -530,42 +553,29 @@
                }

// Show exception details (SQLException node can appear anywhere in the hierarchy)
-               if (p != null && p.getLastPathComponent() instanceof 
SQLExceptionNode) {
+ if (p != null && p.getLastPathComponent() instanceof SQLObject && ((SQLObject) p.getLastPathComponent()).getChildrenInaccessibleReason() != null) {
                        newMenu.addSeparator();
- final SQLExceptionNode node = (SQLExceptionNode) p.getLastPathComponent();
+            final SQLObject node = (SQLObject) p.getLastPathComponent();
newMenu.add(new JMenuItem(new AbstractAction(Messages.getString("DBTree.showExceptionDetails")) { //$NON-NLS-1$
                 public void actionPerformed(ActionEvent e) {
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(), - Messages.getString("DBTree.exceptionNodeReport"), node.getException()); //$NON-NLS-1$ + Messages.getString("DBTree.exceptionNodeReport"), node.getChildrenInaccessibleReason()); //$NON-NLS-1$
                 }
             }));

// If the sole child is an exception node, we offer the user a way to re-try the operation
-            try {
-                final SQLObject parent = node.getParent();
-                if (parent.getChildCount() == 1) {
- newMenu.add(new JMenuItem(new AbstractAction(Messages.getString("DBTree.retryActionName")) { //$NON-NLS-1$
-                        public void actionPerformed(ActionEvent e) {
-                            parent.removeChild(0);
-                            parent.setPopulated(false);
-                            try {
-                                parent.getChildren(); // forces populate
-                            } catch (ArchitectException ex) {
-                                try {
- parent.addChild(new SQLExceptionNode(ex, Messages.getString("DBTree.exceptionDuringRetry"))); //$NON-NLS-1$
-                                                               } catch 
(ArchitectException e1) {
- logger.error("Couldn't add SQLExceptionNode to menu:", e1); //$NON-NLS-1$
-                                                                       
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(),
- Messages.getString("DBTree.failedToAddSQLExceptionNode"), e1); //$NON-NLS-1$
-                                                               }
- SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(), - Messages.getString("DBTree.exceptionDuringRetry"), ex); //$NON-NLS-1$
-                            }
+            if (node.getChildrenInaccessibleReason() != null) {
+ newMenu.add(new JMenuItem(new AbstractAction(Messages.getString("DBTree.retryActionName")) { //$NON-NLS-1$
+                    public void actionPerformed(ActionEvent e) {
+                        node.setPopulated(false);
+                        try {
+                            node.getChildren(); // forces populate
+                        } catch (ArchitectException ex) {
+ SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(), + Messages.getString("DBTree.exceptionDuringRetry"), ex); //$NON-NLS-1$
                         }
-                    }));
-                }
-            } catch (ArchitectException ex) {
- logger.error("Couldn't count siblings of SQLExceptionNode", ex); //$NON-NLS-1$
+                    }
+                }));
             }
                }

@@ -732,13 +742,7 @@
                @Override
                public void cleanup() throws Exception {
                    if (getDoStuffException() != null) {
- // FIXME: SQLObject should have an "exception" property that's not a child, - // and client code shouldn't have to clean up populate exceptions
-                       //        like this
-                       mostRecentlyVisited.addChild(
-                               new SQLExceptionNode(
-                                       getDoStuffException(),
- Messages.getString("DBTree.errorDuringDbProbe"))); //$NON-NLS-1$
+                       throw new RuntimeException(getDoStuffException());
                    }
                }
        }

Modified: trunk/src/ca/sqlpower/architect/swingui/PlayPen.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/PlayPen.java        (original)
+++ trunk/src/ca/sqlpower/architect/swingui/PlayPen.java Thu Dec 11 07:42:05 2008
@@ -96,7 +96,6 @@
 import ca.sqlpower.architect.SQLCatalog;
 import ca.sqlpower.architect.SQLColumn;
 import ca.sqlpower.architect.SQLDatabase;
-import ca.sqlpower.architect.SQLExceptionNode;
 import ca.sqlpower.architect.SQLObject;
 import ca.sqlpower.architect.SQLObjectEvent;
 import ca.sqlpower.architect.SQLObjectListener;
@@ -1591,7 +1590,6 @@
                                                Iterator it = 
sourceSchema.getChildren().iterator();
                                                while (it.hasNext() && 
!isCancelled()) {
                             Object nextTable = it.next();
- if (nextTable instanceof SQLExceptionNode) continue;
                                                        SQLTable sourceTable = 
(SQLTable) nextTable;
                                                        message = 
ArchitectUtils.truncateString(sourceTable.getName());
                                                        TablePane tp = 
importTableCopy(sourceTable, preferredLocation);
@@ -1607,7 +1605,6 @@
                                                                Iterator it = 
sourceSchema.getChildren().iterator();
                                                                while (it.hasNext() 
&& !isCancelled()) {
                                                                        Object 
nextTable = it.next();
- if (nextTable instanceof SQLExceptionNode) continue; SQLTable sourceTable = (SQLTable) nextTable;
                                                                        message 
= ArchitectUtils.truncateString(sourceTable.getName());
                                                                        
TablePane tp = importTableCopy(sourceTable, preferredLocation);
@@ -1618,7 +1615,6 @@
                                                } else {
                                                        while (cit.hasNext() && 
!isCancelled()) {
                                 Object nextTable = cit.next();
- if (nextTable instanceof SQLExceptionNode) continue;
                                                                SQLTable 
sourceTable = (SQLTable) nextTable;
                                                                message = 
ArchitectUtils.truncateString(sourceTable.getName());
                                                                TablePane tp = 
importTableCopy(sourceTable, preferredLocation);

Modified: trunk/src/ca/sqlpower/architect/swingui/SwingUIProject.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/SwingUIProject.java (original)
+++ trunk/src/ca/sqlpower/architect/swingui/SwingUIProject.java Thu Dec 11 07:42:05 2008
@@ -51,7 +51,6 @@
 import ca.sqlpower.architect.SQLCatalog;
 import ca.sqlpower.architect.SQLColumn;
 import ca.sqlpower.architect.SQLDatabase;
-import ca.sqlpower.architect.SQLExceptionNode;
 import ca.sqlpower.architect.SQLIndex;
 import ca.sqlpower.architect.SQLObject;
 import ca.sqlpower.architect.SQLRelationship;
@@ -1171,6 +1170,10 @@
         // properties of all SQLObject types
         propNames.put("physicalName", o.getPhysicalName()); //$NON-NLS-1$
propNames.put("name", o.getName()); // note: there was no name attrib for SQLDatabase, SQLRelationship.ColumnMapping, and SQLExceptionNode //$NON-NLS-1$
+
+        if (o.getChildrenInaccessibleReason() != null) {
+ propNames.put("sql-exception", o.getChildrenInaccessibleReason().getMessage()); //$NON-NLS-1$
+        }

         if (o instanceof SQLDatabase) {
             id = "DB"+sqlObjectSaveIdMap.size(); //$NON-NLS-1$
@@ -1233,10 +1236,6 @@
             type = "column-mapping"; //$NON-NLS-1$
propNames.put("pk-column-ref", sqlObjectSaveIdMap.get(((SQLRelationship.ColumnMapping) o).getPkColumn())); //$NON-NLS-1$ propNames.put("fk-column-ref", sqlObjectSaveIdMap.get(((SQLRelationship.ColumnMapping) o).getFkColumn())); //$NON-NLS-1$
-        } else if (o instanceof SQLExceptionNode) {
-            id = "EXC"+sqlObjectSaveIdMap.size(); //$NON-NLS-1$
-            type = "sql-exception"; //$NON-NLS-1$
- propNames.put("message", ((SQLExceptionNode) o).getMessage()); //$NON-NLS-1$
         } else if (o instanceof SQLIndex) {
             id = "IDX"+sqlObjectSaveIdMap.size(); //$NON-NLS-1$
             type = "index"; //$NON-NLS-1$
@@ -1265,7 +1264,7 @@
throw new UnsupportedOperationException("Whoops, the SQLObject type " //$NON-NLS-1$ +o.getClass().getName()+" is not supported!"); //$NON-NLS-1$
         }
-
+
         sqlObjectSaveIdMap.put(o, id);

         boolean skipChildren = false;
@@ -1273,11 +1272,7 @@
//ioo.print("<"+type+" hashCode=\""+o.hashCode()+"\" id=\""+id+"\" "); // use this for debugging duplicate object problems ioo.print(out, "<"+type+" id="+quote(id)+" "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

- if (o.allowsChildren() && o.isPopulated() && o.getChildCount() == 1 && o.getChild(0) instanceof SQLExceptionNode) { - // if the only child is an exception node, just save the parent as non-populated
-            ioo.niprint(out, "populated=\"false\" "); //$NON-NLS-1$
-            skipChildren = true;
- } else if ( (!getSession().isSavingEntireSource()) && (!o.isPopulated()) ) { + if ( (!getSession().isSavingEntireSource()) && (!o.isPopulated()) ) {
             ioo.niprint(out, "populated=\"false\" "); //$NON-NLS-1$
         } else {
             ioo.niprint(out, "populated=\"true\" "); //$NON-NLS-1$

Modified: trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeCellRenderer.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeCellRenderer.java (original) +++ trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeCellRenderer.java Thu Dec 11 07:42:05 2008
@@ -67,6 +67,7 @@
public static final ImageIcon PK_ICON = new ImageIcon(DBTreeCellRenderer.class.getResource("icons/Index_key16.png")); public static final ImageIcon UNIQUE_INDEX_ICON = new ImageIcon(DBTreeCellRenderer.class.getResource("icons/Index_unique16.png")); public static final ImageIcon COLUMN_ICON = new ImageIcon(DBTreeCellRenderer.class.getResource("icons/Column16.png")); + public static final ImageIcon ERROR_ICON = new ImageIcon(DBTreeCellRenderer.class.getResource("icons/stop16.png"));

private final List<IconFilter> iconFilterChain = new ArrayList<IconFilter>();

@@ -82,7 +83,13 @@
                                                                                
                  int row,
                                                                                
                  boolean hasFocus) {
                setText(value.toString());
-               if (value instanceof SQLDatabase) {
+           setToolTipText(getText());
+       
+ if (value instanceof SQLObject && ((SQLObject) value).getChildrenInaccessibleReason() != null) { + logger.debug("Children are not accessible from the node " + ((SQLObject) value).getName());
+            setIcon(ERROR_ICON);
+ setToolTipText("Inaccessible: " + ((SQLObject) value).getChildrenInaccessibleReason());
+        } else if (value instanceof SQLDatabase) {
                        SQLDatabase db = (SQLDatabase) value;
                        if (db.isPlayPenDatabase()) {
                                setIcon(TARGET_DB_ICON);
@@ -155,7 +162,6 @@
                        setForeground(Color.lightGray);
                    }
                }
-           setToolTipText(getText());
        
            if (value instanceof SQLObject || value == null) {
                for (IconFilter filter : getIconFilterChain()) {

Modified: trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java (original) +++ trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java Thu Dec 11 07:42:05 2008
@@ -35,13 +35,11 @@
 import ca.sqlpower.architect.ArchitectException;
 import ca.sqlpower.architect.ArchitectRuntimeException;
 import ca.sqlpower.architect.ArchitectUtils;
-import ca.sqlpower.architect.SQLExceptionNode;
 import ca.sqlpower.architect.SQLObject;
 import ca.sqlpower.architect.SQLObjectEvent;
 import ca.sqlpower.architect.SQLObjectListener;
 import ca.sqlpower.architect.SQLObjectRoot;
 import ca.sqlpower.architect.SQLRelationship;
-import ca.sqlpower.swingui.SPSUtils;

public class DBTreeModel implements TreeModel, SQLObjectListener, java.io.Serializable {

@@ -78,23 +76,23 @@

        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$
+               SQLObject sqlParent = (SQLObject) parent;
                try {
- if (logger.isDebugEnabled()) logger.debug("returning "+((SQLObject) parent).getChild(index)); //$NON-NLS-1$
-                       return ((SQLObject) parent).getChild(index);
+ if (logger.isDebugEnabled()) logger.debug("returning "+sqlParent.getChild(index)); //$NON-NLS-1$
+                       return sqlParent.getChild(index);
                } catch (Exception e) {
- SQLExceptionNode fakeChild = putExceptionNodeUnder((SQLObject) parent, e);
-                       return fakeChild;
+                   throw new RuntimeException(e);
                }
        }

        public int getChildCount(Object parent) {
if (logger.isDebugEnabled()) logger.debug("DBTreeModel.getChildCount("+parent+")"); //$NON-NLS-1$ //$NON-NLS-2$
+               SQLObject sqlParent = (SQLObject) parent;
                try {
- if (logger.isDebugEnabled()) logger.debug("returning "+((SQLObject) parent).getChildCount()); //$NON-NLS-1$
-                       return ((SQLObject) parent).getChildCount();
+ if (logger.isDebugEnabled()) logger.debug("returning "+sqlParent.getChildCount()); //$NON-NLS-1$
+                       return sqlParent.getChildCount();
                } catch (Exception e) {
-                       putExceptionNodeUnder((SQLObject) parent, e);
- return 1; // XXX: could be incorrect if exception was not a populate problem!
+                   throw new RuntimeException(e);
                }
        }

@@ -254,49 +252,6 @@
            return nodePaths;
        }

-       /**
-        * Creates a SQLExceptionNode with the given Throwable and places
-        * it under parent.
-        *
-        * @return the node that has been added to parent.
-        */
- protected SQLExceptionNode putExceptionNodeUnder(final SQLObject parent, Throwable ex) {
-               // dig for root cause and message
-               logger.info("Adding exception node under "+parent, ex); 
//$NON-NLS-1$
-               String message = ex.getMessage();
-               Throwable cause = ex;
-               while (cause.getCause() != null) {
-                       cause = cause.getCause();
-                       if (cause.getMessage() != null && 
cause.getMessage().length() > 0) {
-                               message = cause.getMessage();
-                       }
-               }
-               
-               if (message == null || message.length() == 0) {
-                       message = "Check application log for details"; 
//$NON-NLS-1$
-               }
-               
-               final SQLExceptionNode excNode = new SQLExceptionNode(ex, 
message);
-               excNode.setParent((SQLObject) parent);
-
- /* This is likely to fail, but it should convince the parent that it is populated */
-               try {
-                       parent.getChildCount();
-               } catch (ArchitectException e) {
- logger.error("Couldn't populate parent node of exception"); //$NON-NLS-1$
-               }
-
-               try {
-                       for(int i=0; i< parent.getChildCount(); i++){
-                               parent.removeChild(0);
-                       }
-                       parent.addChild(excNode);
-               } catch (ArchitectException e) {
- logger.error("Couldn't add SQLExceptionNode \""+excNode.getName()+"\" to tree model:", e); //$NON-NLS-1$ //$NON-NLS-2$ - SPSUtils.showExceptionDialogNoReport("Failed to add SQLExceptionNode to tree model.", e); //$NON-NLS-1$
-               }
-               return excNode;
-       }

// --------------------- SQLObject listener support -----------------------
        public void dbChildrenInserted(SQLObjectEvent e) {

Added: trunk/src/ca/sqlpower/architect/swingui/dbtree/icons/stop16.png
==============================================================================
Binary file. No diff available.

Reply via email to