Revision: 3370
Author: [email protected]
Date: Wed Mar 10 08:21:50 2010
Log: Updated the Architect and SQLObjects to push all events onto the
foreground thread. Previously the current thread was
always considered the foreground thread.
Changes to push events to the foreground thread:
- Populate on the SQLObjects now has all of the objects created on the
foreground thread.
- All of the columns in all tables get populated at the same time. This
improves the performance of populating columns.
- As a side effect the indices of tables will not always be populated
immediately after columns are populated so
there are more places where populateIndices are called in SQLTable.
- The correct session is used by the ArchitectProject to get the foreground
thread.
Additional changes made to correct this problem:
- The getSession on the ArchitectProject throws a correct exception when
the session is null.
- The childrenInaccessibleReason has been updated to allow an exception for
each child type of the objects.
- The fix to preventing an infinite loop between populate and the
DBTreeModel has been updated.
Now the tree model will collect events and update the tree at the end of
a transaction.
http://code.google.com/p/power-architect/source/detail?r=3370
Modified:
/trunk
/trunk/regress/ca/sqlpower/architect/ArchitectProjectTest.java
/trunk/regress/ca/sqlpower/architect/profile/ProfileManagerImplTest.java
/trunk/regress/ca/sqlpower/architect/profile/TestProfileBase.java
/trunk/regress/ca/sqlpower/architect/swingui/TestSwingUIProject.java
/trunk/regress/ca/sqlpower/architect/swingui/TestingArchitectSwingSession.java
/trunk/regress/ca/sqlpower/architect/util/ArchitectNewValueMaker.java
/trunk/src/ca/sqlpower/architect/ArchitectProject.java
/trunk/src/ca/sqlpower/architect/ArchitectSessionImpl.java
/trunk/src/ca/sqlpower/architect/ProjectLoader.java
/trunk/src/ca/sqlpower/architect/messages.properties
/trunk/src/ca/sqlpower/architect/profile/ProfileManagerImpl.java
/trunk/src/ca/sqlpower/architect/swingui/ArchitectSwingSession.java
/trunk/src/ca/sqlpower/architect/swingui/ArchitectSwingSessionImpl.java
/trunk/src/ca/sqlpower/architect/swingui/DBTree.java
/trunk/src/ca/sqlpower/architect/swingui/PlayPen.java
/trunk/src/ca/sqlpower/architect/swingui/SwingUIProjectLoader.java
/trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeCellRenderer.java
/trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java
=======================================
--- /trunk/regress/ca/sqlpower/architect/ArchitectProjectTest.java Tue Feb
16 14:02:41 2010
+++ /trunk/regress/ca/sqlpower/architect/ArchitectProjectTest.java Wed Mar
10 08:21:50 2010
@@ -38,7 +38,7 @@
super.setUp();
ArchitectSession session = new StubArchitectSession();
objectUnderTest = new ArchitectProject();
- objectUnderTest.init(session);
+ objectUnderTest.setSession(session);
getRootObject().addChild(objectUnderTest, 0);
}
=======================================
---
/trunk/regress/ca/sqlpower/architect/profile/ProfileManagerImplTest.java
Wed Feb 17 15:05:17 2010
+++
/trunk/regress/ca/sqlpower/architect/profile/ProfileManagerImplTest.java
Wed Mar 10 08:21:50 2010
@@ -41,9 +41,14 @@
super.setUp();
profileManager = new ProfileManagerImpl();
- ArchitectProject project = (ArchitectProject) new
ArchitectNewValueMaker(
+ final ArchitectProject project = (ArchitectProject) new
ArchitectNewValueMaker(
getRootObject(),
getPLIni()).makeNewValue(ArchitectProject.class, null, "");
- project.init(new StubArchitectSession());
+ project.setSession(new StubArchitectSession() {
+ @Override
+ public ArchitectProject getWorkspace() {
+ return project;
+ }
+ });
project.setProfileManager(profileManager);
getRootObject().addChild(project, 0);
=======================================
--- /trunk/regress/ca/sqlpower/architect/profile/TestProfileBase.java Fri
Feb 12 07:43:48 2010
+++ /trunk/regress/ca/sqlpower/architect/profile/TestProfileBase.java Wed
Mar 10 08:21:50 2010
@@ -176,7 +176,6 @@
assertNotNull(t3);
pm = new ProfileManagerImpl();
- ((ProfileManagerImpl) pm).setUserPrompterFactory(session);
session.getWorkspace().setProfileManager(pm);
pm.getDefaultProfileSettings().setFindingAvg(true);
pm.getDefaultProfileSettings().setFindingMin(true);
=======================================
--- /trunk/regress/ca/sqlpower/architect/swingui/TestSwingUIProject.java
Thu Mar 4 15:28:57 2010
+++ /trunk/regress/ca/sqlpower/architect/swingui/TestSwingUIProject.java
Wed Mar 10 08:21:50 2010
@@ -483,7 +483,7 @@
// 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());
+ System.out.println("DB has child exception " +
db.getChildrenInaccessibleReasons());
Map<String, Object> newDescription =
ca.sqlpower.testutil.TestUtils.getAllInterestingProperties(db,
propertiesToIgnore);
=======================================
---
/trunk/regress/ca/sqlpower/architect/swingui/TestingArchitectSwingSession.java
Thu Feb 25 09:46:15 2010
+++
/trunk/regress/ca/sqlpower/architect/swingui/TestingArchitectSwingSession.java
Wed Mar 10 08:21:50 2010
@@ -41,7 +41,6 @@
import ca.sqlpower.architect.olap.OLAPRootObject;
import ca.sqlpower.architect.olap.OLAPSession;
import ca.sqlpower.architect.profile.ProfileManager;
-import ca.sqlpower.architect.profile.ProfileManagerImpl;
import
ca.sqlpower.architect.swingui.ArchitectSwingSessionImpl.ColumnVisibility;
import ca.sqlpower.architect.swingui.olap.OLAPEditSession;
import ca.sqlpower.architect.undo.ArchitectUndoManager;
@@ -98,7 +97,7 @@
}
};
this.delegateSession = new ArchitectSessionImpl(context, "test");
- ((ProfileManagerImpl)
delegateSession.getProfileManager()).setUserPrompterFactory(this);
+ delegateSession.getWorkspace().setSession(this);
project = new SwingUIProjectLoader(this);
userSettings = context.getUserSettings();
sourceDatabases = new DBTree(this);
@@ -384,18 +383,19 @@
}
public boolean isForegroundThread() {
- // TODO Auto-generated method stub
- return false;
+ return true;
}
public void runInBackground(Runnable runner) {
- // TODO Auto-generated method stub
-
+ runner.run();
+ }
+
+ public void runInBackground(Runnable runner, String threadName) {
+ runner.run();
}
public void runInForeground(Runnable runner) {
- // TODO Auto-generated method stub
-
+ runner.run();
}
public void addPropertyChangeListener(PropertyChangeListener l) {
=======================================
--- /trunk/regress/ca/sqlpower/architect/util/ArchitectNewValueMaker.java
Thu Feb 18 07:51:21 2010
+++ /trunk/regress/ca/sqlpower/architect/util/ArchitectNewValueMaker.java
Wed Mar 10 08:21:50 2010
@@ -31,7 +31,6 @@
import ca.sqlpower.sqlobject.SQLObjectException;
import ca.sqlpower.sqlobject.SQLTable;
import ca.sqlpower.testutil.GenericNewValueMaker;
-import ca.sqlpower.util.SPSession;
public class ArchitectNewValueMaker extends GenericNewValueMaker {
@@ -69,12 +68,7 @@
ArchitectProject project;
final SPObject rootObject = getRootObject();
try {
- project = new ArchitectProject() {
- @Override
- public SPSession getSession() {
- return rootObject.getSession();
- }
- };
+ project = new ArchitectProject();
} catch (SQLObjectException e) {
throw new RuntimeException(e);
}
=======================================
--- /trunk/src/ca/sqlpower/architect/ArchitectProject.java Tue Feb 16
14:02:41 2010
+++ /trunk/src/ca/sqlpower/architect/ArchitectProject.java Wed Mar 10
08:21:50 2010
@@ -31,6 +31,7 @@
import ca.sqlpower.object.annotation.Accessor;
import ca.sqlpower.object.annotation.Constructor;
import ca.sqlpower.object.annotation.ConstructorParameter;
+import ca.sqlpower.object.annotation.Mutator;
import ca.sqlpower.object.annotation.NonBound;
import ca.sqlpower.object.annotation.NonProperty;
import ca.sqlpower.object.annotation.Transient;
@@ -40,7 +41,7 @@
import ca.sqlpower.sqlobject.SQLObject;
import ca.sqlpower.sqlobject.SQLObjectException;
import ca.sqlpower.sqlobject.SQLObjectRoot;
-import ca.sqlpower.util.SPSession;
+import ca.sqlpower.util.SessionNotFoundException;
/**
*
@@ -68,6 +69,11 @@
private final SQLObjectRoot rootObject;
private ProfileManager profileManager;
+ /**
+ * The current integrity watcher on the project.
+ */
+ private SourceObjectIntegrityWatcher currentWatcher;
+
/**
* Constructs an architect project. The init method must be called
immediately
* after creating a project.
@@ -93,16 +99,24 @@
public
ArchitectProject(@ConstructorParameter(isProperty=ParameterType.CHILD,
propertyName="rootObject") SQLObjectRoot rootObject)
throws SQLObjectException {
this.rootObject = rootObject;
+ rootObject.setParent(this);
+ setName("Architect Project");
}
/**
* Call this to initialize the session and the children of the project.
*/
- public void init(ArchitectSession session) {
+ @Transient @Mutator
+ public void setSession(ArchitectSession session) {
+ if (this.session != null) {
+ rootObject.removeSQLObjectPreEventListener(currentWatcher);
+ currentWatcher = null;
+ }
this.session = session;
- rootObject.addSQLObjectPreEventListener(new
SourceObjectIntegrityWatcher(session));
- rootObject.setParent(this);
- setName("Architect Project");
+ if (this.session != null) {
+ currentWatcher = new SourceObjectIntegrityWatcher(session);
+ rootObject.addSQLObjectPreEventListener(currentWatcher);
+ }
}
/**
@@ -187,8 +201,13 @@
}
@Transient @Accessor
- public SPSession getSession() {
- return session;
+ public ArchitectSession getSession() throws SessionNotFoundException {
+ if (session != null) {
+ return session;
+ } else {
+ throw new SessionNotFoundException("The project has not been
given a session yet. " +
+ "This should only happen during the construction of the
project.");
+ }
}
public boolean allowsChildren() {
=======================================
--- /trunk/src/ca/sqlpower/architect/ArchitectSessionImpl.java Thu Feb 25
09:46:15 2010
+++ /trunk/src/ca/sqlpower/architect/ArchitectSessionImpl.java Wed Mar 10
08:21:50 2010
@@ -87,9 +87,8 @@
this.context = context;
this.project = new ArchitectProject();
- project.init(this);
+ project.setSession(this);
ProfileManagerImpl manager = new ProfileManagerImpl();
- manager.setUserPrompterFactory(this);
this.project.setProfileManager(manager);
this.name = name;
this.projectLoader = new ProjectLoader(this);
@@ -210,7 +209,7 @@
public void runInForeground(Runnable runner) {
runner.run();
}
-
+
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
=======================================
--- /trunk/src/ca/sqlpower/architect/ProjectLoader.java Thu Mar 4 09:45:30
2010
+++ /trunk/src/ca/sqlpower/architect/ProjectLoader.java Wed Mar 10 08:21:50
2010
@@ -65,6 +65,7 @@
import ca.sqlpower.sqlobject.SQLIndex.AscendDescend;
import ca.sqlpower.sqlobject.SQLIndex.Column;
import ca.sqlpower.sqlobject.SQLRelationship.Deferrability;
+import ca.sqlpower.sqlobject.SQLRelationship.SQLImportedKey;
import ca.sqlpower.sqlobject.SQLRelationship.UpdateDeleteRule;
import ca.sqlpower.swingui.SPSUtils;
import ca.sqlpower.util.BrowserUtil;
@@ -95,7 +96,7 @@
String message = attr.getValue("sql-exception");
if (message != null) {
try {
- obj.setChildrenInaccessibleReason(new
SQLObjectException(message), false);
+ obj.setChildrenInaccessibleReason(new
SQLObjectException(message), SQLObject.class, false);
} catch (SQLObjectException e) {
throw new AssertionError("Unreachable code");
}
@@ -628,10 +629,10 @@
return tab;
}
}
-
+
/**
- * XXX Temporary factory for folders until the file format changes and
the folders
- * are removed permanently.
+ * XXX Temporary factory for folders until the file format changes and
the
+ * folders are removed permanently.
*/
private class SQLFolderFactory extends AbstractObjectCreationFactory {
@Override
@@ -639,14 +640,48 @@
String type = attributes.getValue("type"); //1=col, 2=import,
3=export, 4=index
boolean isPopulated =
Boolean.valueOf(attributes.getValue("populated"));
+ String message = attributes.getValue("sql-exception");
+
if (type.equals("1")) {
currentTable.setColumnsPopulated(isPopulated);
+ if (message != null) {
+ try {
+ currentTable.setChildrenInaccessibleReason(new
SQLObjectException(message),
+ SQLColumn.class, false);
+ } catch (SQLObjectException e) {
+ throw new AssertionError("Unreachable code");
+ }
+ }
} else if (type.equals("2")) {
currentTable.setImportedKeysPopulated(isPopulated);
+ if (message != null) {
+ try {
+ currentTable.setChildrenInaccessibleReason(new
SQLObjectException(message),
+ SQLImportedKey.class, false);
+ } catch (SQLObjectException e) {
+ throw new AssertionError("Unreachable code");
+ }
+ }
} else if (type.equals("3")) {
currentTable.setExportedKeysPopulated(isPopulated);
+ if (message != null) {
+ try {
+ currentTable.setChildrenInaccessibleReason(new
SQLObjectException(message),
+ SQLRelationship.class, false);
+ } catch (SQLObjectException e) {
+ throw new AssertionError("Unreachable code");
+ }
+ }
} else if (type.equals("4")) {
currentTable.setIndicesPopulated(isPopulated);
+ if (message != null) {
+ try {
+ currentTable.setChildrenInaccessibleReason(new
SQLObjectException(message),
+ SQLIndex.class, false);
+ } catch (SQLObjectException e) {
+ throw new AssertionError("Unreachable code");
+ }
+ }
}
return currentTable;
=======================================
--- /trunk/src/ca/sqlpower/architect/messages.properties Thu Aug 7
09:26:17 2008
+++ /trunk/src/ca/sqlpower/architect/messages.properties Wed Mar 10
08:21:50 2010
@@ -1,4 +1,8 @@
cancel=Cancel
+columnsUnpopulatedOnLoad=The columns of this table were not populated in
the loaded file. To populate these columns refresh the data source.
+importedKeysUnpopulatedOnLoad=The imported keys of this table were not
populated in the loaded file. To populate these columns refresh the data
source.
+exportedKeysUnpopulatedOnLoad=The exported keys of this table were not
populated in the loaded file. To populate these columns refresh the data
source.
+indicesUnpopulatedOnLoad=The indices of this table were not populated in
the loaded file. To populate these columns refresh the data source.
ok=OK
SourceObjectIntegrityWatcher.forgetLineageOption=Forget Lineage
SourceObjectIntegrityWatcher.keepSourceConnectionOption=Keep Source
Connection
=======================================
--- /trunk/src/ca/sqlpower/architect/profile/ProfileManagerImpl.java Thu
Feb 18 13:18:27 2010
+++ /trunk/src/ca/sqlpower/architect/profile/ProfileManagerImpl.java Wed
Mar 10 08:21:50 2010
@@ -32,7 +32,6 @@
import org.apache.log4j.Logger;
import ca.sqlpower.architect.ArchitectProject;
-import ca.sqlpower.architect.ArchitectSession;
import ca.sqlpower.architect.profile.event.ProfileChangeEvent;
import ca.sqlpower.architect.profile.event.ProfileChangeListener;
import ca.sqlpower.object.AbstractSPObject;
@@ -50,7 +49,6 @@
import ca.sqlpower.sqlobject.SQLObjectPreEventListener;
import ca.sqlpower.sqlobject.SQLTable;
import ca.sqlpower.util.UserPrompter;
-import ca.sqlpower.util.UserPrompterFactory;
import ca.sqlpower.util.UserPrompter.UserPromptOptions;
import ca.sqlpower.util.UserPrompter.UserPromptResponse;
import ca.sqlpower.util.UserPrompterFactory.UserPromptType;
@@ -82,7 +80,7 @@
public void dbChildrenPreRemove(SQLObjectPreEvent e) {
logger.debug("Pre-remove on profile manager");
- UserPrompter up = session.createUserPrompter(
+ UserPrompter up = getParent().getSession().createUserPrompter(
"{0} tables have been profiled from the database
{1}.\n" +
"\n" +
"If you proceed, the profiling information from the
database" +
@@ -130,11 +128,6 @@
*/
private final List<TableProfileResult> results = new
ArrayList<TableProfileResult>();
- /**
- * The user prompter for the profile manager.
- */
- private UserPrompterFactory session;
-
/**
* The defaults that new profile results will be created with.
* XXX these are specific to the remote database profiler!
@@ -202,11 +195,6 @@
defaultProfileSettings.setParent(this);
setName("Profile Manager");
}
-
- @Transient @Mutator
- public void setUserPrompterFactory(ArchitectSession session) {
- this.session = session;
- }
@Override @Mutator
public void setParent(SPObject parent) {
@@ -215,8 +203,9 @@
((ArchitectProject)
getParent()).getRootObject().removeSQLObjectPreEventListener(databaseRemovalWatcher);
}
super.setParent(parent);
- if (parent != null && ((ArchitectProject)
parent).getRootObject() != null) {
- ((ArchitectProject)
parent).getRootObject().addSQLObjectPreEventListener(databaseRemovalWatcher);
+ final ArchitectProject architectProject = (ArchitectProject)
parent;
+ if (parent != null && architectProject.getRootObject() != null) {
+
architectProject.getRootObject().addSQLObjectPreEventListener(databaseRemovalWatcher);
}
firePropertyChange("parent", oldParent, parent);
}
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/ArchitectSwingSession.java Thu
Feb 25 09:46:15 2010
+++ /trunk/src/ca/sqlpower/architect/swingui/ArchitectSwingSession.java Wed
Mar 10 08:21:50 2010
@@ -329,4 +329,12 @@
public boolean isEnterpriseSession();
public ArchitectClientSideSession getEnterpriseSession();
-}
+
+ /**
+ * Works the same as {...@link #runInBackground(Runnable)} but lets you
+ * name the background thread for easier debugging.
+ * @param runner
+ * @param threadName
+ */
+ public void runInBackground(Runnable runner, String threadName);
+}
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/ArchitectSwingSessionImpl.java
Thu Mar 4 09:45:30 2010
+++ /trunk/src/ca/sqlpower/architect/swingui/ArchitectSwingSessionImpl.java
Wed Mar 10 08:21:50 2010
@@ -261,9 +261,9 @@
this.isNew = true;
this.context = context;
this.delegateSession = delegateSession;
+ delegateSession.getWorkspace().setSession(this);
this.olapRootObject = new OLAPRootObject(delegateSession);
ProfileManagerImpl profileManager = new ProfileManagerImpl();
- profileManager.setUserPrompterFactory(this);
((ArchitectSessionImpl)delegateSession).setProfileManager(profileManager);
((ArchitectSessionImpl)delegateSession).setUserPrompterFactory(this);
this.recent = new RecentMenu(this.getClass()) {
@@ -1086,34 +1086,33 @@
}
public boolean isForegroundThread() {
- return SwingUtilities.isEventDispatchThread();
+ //Until the GUI is initialized we may be running headless in which
case
+ //we will not be using the EDT.
+ if (frame != null) {
+ return SwingUtilities.isEventDispatchThread();
+ } else {
+ return true;
+ }
}
- public void runInBackground(final Runnable runner) {
- SPSwingWorker worker = new SPSwingWorker(this) {
-
- @Override
- public void doStuff() throws Exception {
- runner.run();
- }
-
- @Override
- public void cleanup() throws Exception {
- //do nothing
- }
-
- };
- new Thread(worker).start();
+ public void runInBackground(Runnable runner) {
+ runInBackground(runner, "worker");
+ }
+
+ public void runInBackground(final Runnable runner, String name) {
+ new Thread(runner, name).start();
}
public void runInForeground(Runnable runner) {
- if (SwingUtilities.isEventDispatchThread()) {
+ //Until the GUI is initialized we may be running headless in which
case
+ //we will not be using the EDT.
+ if (frame == null || SwingUtilities.isEventDispatchThread()) {
runner.run();
} else {
SwingUtilities.invokeLater(runner);
}
}
-
+
public void addPropertyChangeListener(PropertyChangeListener l) {
delegateSession.addPropertyChangeListener(l);
}
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/DBTree.java Thu Mar 4
14:42:47 2010
+++ /trunk/src/ca/sqlpower/architect/swingui/DBTree.java Wed Mar 10
08:21:50 2010
@@ -160,9 +160,12 @@
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) {
+ if (e.getClickCount() == 2 && node instanceof
SQLObject &&
+ !((SQLObject)
node).getChildrenInaccessibleReasons().isEmpty()) {
+ Throwable firstException = ((SQLObject) node).
+
getChildrenInaccessibleReasons().entrySet().iterator().next().getValue();
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(),
-
Messages.getString("DBTree.exceptionNodeReport"), ((SQLObject)
node).getChildrenInaccessibleReason()); //$NON-NLS-1$
+
Messages.getString("DBTree.exceptionNodeReport"), firstException);
//$NON-NLS-1$
}
}
}
@@ -286,7 +289,7 @@
session.getArchitectFrame().setCursor(null);
}
}
-
+
// ---------- methods of DragSourceListener -----------
public void dragEnter(DragSourceDragEvent dsde) {
logger.debug("DBTree: got dragEnter event"); //$NON-NLS-1$
@@ -555,7 +558,7 @@
//tree only has one child
if (!tempDB.isCatalogContainer() && !tempDB.isSchemaContainer()
&&
(!(tempDB.getChildCount() == 1) ||
-
tempDB.getChildrenInaccessibleReason() == null))
+
tempDB.getChildrenInaccessibleReasons().isEmpty()))
{
//a new action is needed to maintain the
database variable
CompareToCurrentAction compareToCurrentAction = new
CompareToCurrentAction();
@@ -618,18 +621,21 @@
}
// Show exception details (SQLException node can appear anywhere in the
hierarchy)
- if (p != null && p.getLastPathComponent() instanceof SQLObject &&
((SQLObject) p.getLastPathComponent()).getChildrenInaccessibleReason() !=
null) {
+ if (p != null && p.getLastPathComponent() instanceof SQLObject
&&
+ !((SQLObject)
p.getLastPathComponent()).getChildrenInaccessibleReasons().isEmpty()) {
newMenu.addSeparator();
final SQLObject node = (SQLObject) p.getLastPathComponent();
newMenu.add(new JMenuItem(new
AbstractAction(Messages.getString("DBTree.showExceptionDetails")) {
//$NON-NLS-1$
public void actionPerformed(ActionEvent e) {
+ Throwable firstException = ((SQLObject) node).
+
getChildrenInaccessibleReasons().entrySet().iterator().next().getValue();
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(),
-
Messages.getString("DBTree.exceptionNodeReport"),
node.getChildrenInaccessibleReason()); //$NON-NLS-1$
+
Messages.getString("DBTree.exceptionNodeReport"), firstException);
//$NON-NLS-1$
}
}));
// If the sole child is an exception node, we offer the user a
way to re-try the operation
- if (node.getChildrenInaccessibleReason() != null) {
+ if (!node.getChildrenInaccessibleReasons().isEmpty()) {
newMenu.add(new JMenuItem(new
AbstractAction(Messages.getString("DBTree.retryActionName")) { //$NON-NLS-1$
public void actionPerformed(ActionEvent e) {
node.setPopulated(false);
@@ -709,7 +715,7 @@
// start a thread to poke the new SQLDatabase object...
logger.debug("start poking database " + newDB.getName());
//$NON-NLS-1$
PokeDBWorker poker = new PokeDBWorker(newDB);
- new Thread(poker, "PokeDB: " + newDB.getName()).start();
//$NON-NLS-1$
+ session.runInBackground(poker, "PokeDB: " + newDB.getName());
//$NON-NLS-1$
} else {
JOptionPane.showMessageDialog(DBTree.this,
Messages.getString("DBTree.cannotAddConnectionType",
dbcs.getClass().toString()),
Messages.getString("DBTree.cannotAddConnectionTypeTitle"),
JOptionPane.INFORMATION_MESSAGE);
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/PlayPen.java Thu Mar 4
08:08:10 2010
+++ /trunk/src/ca/sqlpower/architect/swingui/PlayPen.java Wed Mar 10
08:21:50 2010
@@ -1493,24 +1493,45 @@
public void doStuff() {
logger.info("AddObjectsTask starting on
thread "+Thread.currentThread().getName()); //$NON-NLS-1$
session.getArchitectFrame().getContentPane().setCursor(new
Cursor(Cursor.WAIT_CURSOR));
+
try {
- int tableCount = 0;
-
- Iterator<SQLObject> soIt =
sqlObjects.iterator();
- // first pass: figure out how much work we need
to do...
- while (soIt.hasNext() && !isCancelled()) {
- SQLObject so = soIt.next();
- tableCount += SQLObjectUtils.countTablesSnapshot(so);
- }
- setJobSize(new Integer(tableCount));
-
- ensurePopulated(sqlObjects);
-
+ Iterator<SQLObject> soIt = sqlObjects.iterator();
+ // first pass: Cause all of the SQLObjects between
the given
+ // ones and the table descendents to populate...
+ while (soIt.hasNext() && !isCancelled()) {
+ SQLObject so = soIt.next();
+ SQLObjectUtils.countTablesSnapshot(so);
+ }
} catch (SQLObjectException e) {
- logger.error("Unexpected exception during
populate", e); //$NON-NLS-1$
+ logger.error("Unexpected exception during populate", e);
//$NON-NLS-1$
setDoStuffException(e);
- errorMessage = "Unexpected exception during populate: " +
e.getMessage(); //$NON-NLS-1$
- }
+ errorMessage = "Unexpected exception during populate: " +
e.getMessage(); //$NON-NLS-1$
+ }
+
+ //Second pass: count the tables. Done in the foreground
to
+ //wait for the objects to be fully populated by pass 1.
+ session.runInForeground(new Runnable() {
+ public void run() {
+ try {
+ int tableCount = 0;
+ Iterator<SQLObject> soIt =
sqlObjects.iterator();
+ while (soIt.hasNext() && !isCancelled()) {
+ SQLObject so = soIt.next();
+ tableCount +=
SQLObjectUtils.countTablesSnapshot(so);
+ }
+ setJobSize(new Integer(tableCount));
+ } catch (SQLObjectException e) {
+ logger.error("Unexpected exception, objects should be
populated by " +
+ "this pass.", e); //$NON-NLS-1$
+ setDoStuffException(e);
+ errorMessage = "Unexpected exception, objects should be
populated " +
+ "by this pass: " +
e.getMessage(); //$NON-NLS-1$
+ }
+ }
+ });
+
+ ensurePopulated(sqlObjects);
+
logger.info("AddObjectsTask done"); //$NON-NLS-1$
}
@@ -1525,7 +1546,15 @@
private void ensurePopulated(List<? extends SQLObject> soList) {
for (SQLObject so : soList) {
if (isCancelled()) break;
- if (so instanceof SQLTable)
setProgress(getProgress() + 1);
+ if (so instanceof SQLTable) {
+ //pushing updates to foreground as population happens on the
foreground
+ //and this will keep the progress bar more honest with what is
happening.
+ session.runInForeground(new Runnable(){
+ public void run() {
+ setProgress(getProgress() + 1);
+ }
+ });
+ }
ensurePopulated(so.getChildren());
}
}
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/SwingUIProjectLoader.java Thu
Mar 4 09:30:17 2010
+++ /trunk/src/ca/sqlpower/architect/swingui/SwingUIProjectLoader.java Wed
Mar 10 08:21:50 2010
@@ -80,6 +80,7 @@
import ca.sqlpower.sqlobject.SQLRelationship;
import ca.sqlpower.sqlobject.SQLSchema;
import ca.sqlpower.sqlobject.SQLTable;
+import ca.sqlpower.sqlobject.SQLRelationship.SQLImportedKey;
import ca.sqlpower.util.ExceptionReport;
import ca.sqlpower.util.SQLPowerUtils;
import ca.sqlpower.util.UserPrompter;
@@ -1265,8 +1266,11 @@
propNames.put("name", o.getName()); // note: there was no name
attrib for SQLDatabase, SQLRelationship.ColumnMapping, and SQLExceptionNode
//$NON-NLS-1$
propNames.put("UUID", o.getUUID());
- if (o.getChildrenInaccessibleReason() != null) {
- propNames.put("sql-exception",
o.getChildrenInaccessibleReason().getMessage()); //$NON-NLS-1$
+ if (!o.getChildrenInaccessibleReasons().isEmpty()) {
+ //Only storing the top exception to prevent file format changes
+ //Only the SQLTable should have multiple children inaccessible
reasons.
+ Throwable topException =
o.getChildrenInaccessibleReason(SQLObject.class);
+ propNames.put("sql-exception", topException); //$NON-NLS-1$
}
if (o instanceof SQLDatabase) {
@@ -1394,20 +1398,47 @@
String indicesFolder = null;
if (o instanceof SQLTable) {
SQLTable table = (SQLTable) o;
+ String exception;
+ if
(table.getChildrenInaccessibleReason(SQLColumn.class) != null) {
+ exception = "sql-exception=\"" +
+ table.getChildrenInaccessibleReason(SQLColumn.class)
+ "\" ";
+ } else {
+ exception = "";
+ }
ioo.println(out, "<folder id=\"FOL" + id + "1\"
populated=\"" +
table.isColumnsPopulated() + "\"
name=\"Columns\" " +
- "physicalName=\"Columns\" type=\"1\">");
+ "physicalName=\"Columns\" " + exception
+ "type=\"1\">");
ioo.indent++;
+ if
(table.getChildrenInaccessibleReason(SQLImportedKey.class) != null) {
+ exception = "sql-exception=\"" +
+
table.getChildrenInaccessibleReason(SQLImportedKey.class) + "\" ";
+ } else {
+ exception = "";
+ }
importedKeysFolder = "<folder id=\"FOL" + id + "2\"
populated=\"" +
table.isImportedKeysPopulated() + "\" name=\"Imported
Keys\" " +
- "physicalName=\"Imported Keys\" type=\"2\">";
+ "physicalName=\"Imported Keys\" " + exception
+ "type=\"2\">";
+
+ if
(table.getChildrenInaccessibleReason(SQLRelationship.class) != null) {
+ exception = "sql-exception=\"" +
+
table.getChildrenInaccessibleReason(SQLRelationship.class) + "\" ";
+ } else {
+ exception = "";
+ }
exportedKeysFolder = "<folder id=\"FOL" + id + "3\"
populated=\"" +
table.isExportedKeysPopulated() + "\" name=\"Exported
Keys\" " +
- "physicalName=\"Exported Keys\" type=\"3\">";
+ "physicalName=\"Exported Keys\" " + exception
+ "type=\"3\">";
+
+ if (table.getChildrenInaccessibleReason(SQLIndex.class) !=
null) {
+ exception = "sql-exception=\"" +
+ table.getChildrenInaccessibleReason(SQLIndex.class)
+ "\" ";
+ } else {
+ exception = "";
+ }
indicesFolder = "<folder id=\"FOL" + id + "4\"
populated=\"" +
table.isIndicesPopulated() + "\" name=\"Indices\" " +
- "physicalName=\"Indices\" type=\"4\">";
+ "physicalName=\"Indices\" " + exception
+ "type=\"4\">";
}
while (children.hasNext()) {
SQLObject child = (SQLObject) children.next();
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeCellRenderer.java
Tue Dec 22 12:38:24 2009
+++ /trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeCellRenderer.java
Wed Mar 10 08:21:50 2010
@@ -147,14 +147,14 @@
setIcon(null);
}
- if (value instanceof SQLObject && ((SQLObject)
value).getChildrenInaccessibleReason() != null) {
+ if (value instanceof SQLObject && !((SQLObject)
value).getChildrenInaccessibleReasons().isEmpty()) {
logger.debug("Children are not accessible from the node " +
((SQLObject) value).getName());
if (getIcon() == null) {
setIcon(ERROR_BADGE);
} else {
setIcon(new ComposedIcon(Arrays.asList(getIcon(),
ERROR_BADGE)));
}
- setToolTipText("Inaccessible: " + ((SQLObject)
value).getChildrenInaccessibleReason());
+ setToolTipText("Inaccessible: " + ((SQLObject)
value).getChildrenInaccessibleReasons());
}
this.selected = sel;
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java Thu
Mar 4 14:42:47 2010
+++ /trunk/src/ca/sqlpower/architect/swingui/dbtree/DBTreeModel.java Wed
Mar 10 08:21:50 2010
@@ -176,8 +176,26 @@
throw new RuntimeException(e);
}
}
+
+ @Override
+ public Throwable getChildrenInaccessibleReason(Class<? extends
SQLObject> childType) {
+ if (childType == containingChildType || childType ==
SQLObject.class) {
+ return
parentTable.getChildrenInaccessibleReason(containingChildType);
+ } else {
+ return null;
+ }
+ }
}
+
+ /**
+ * For use in the {...@link DBTreeSPListener}.
+ */
+ private enum EventType {
+ INSERT,
+ REMOVE,
+ CHANGE
+ }
/**
* A {...@link SPListener} implementation that will fire tree events as the
underlying
@@ -185,7 +203,48 @@
*/
private class DBTreeSPListener implements SPListener {
+ /**
+ * Small inner class to group the tree event with the type of event
it is.
+ */
+ private class TreeEventWithType {
+ private final TreeModelEvent evt;
+ private final EventType type;
+
+ public TreeEventWithType(TreeModelEvent evt, EventType type) {
+ this.evt = evt;
+ this.type = type;
+ }
+
+ public TreeModelEvent getEvt() {
+ return evt;
+ }
+
+ public EventType getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return evt.toString() + ", " + type;
+ }
+ }
+
+ /**
+ * List to hold all of the events. The events are to be pooled
during a
+ * transaction and only acted upon when the transaction is
complete.
+ * This gives methods like populate the ability to fire multiple
child
+ * events but not cause the tree model to get the children of the
+ * objects it is looking at which would cause populate to start
over
+ * again when it is already in the middle of populating.
+ */
+ private List<TreeEventWithType> allEvents = new
ArrayList<TreeEventWithType>();
+
+ private int transactionCount = 0;
+
public void childAdded(SPChildEvent e) {
+ if (!root.getSession().isForegroundThread())
+ throw new IllegalStateException("Adding a child " +
e.getChild() + " to " + e.getSource() +
+ " not on the foreground thread.");
if (logger.isDebugEnabled()) {
logger.debug("dbChildrenInserted. source="+e.getSource()
//$NON-NLS-1$
+" index: "+e.getIndex() //$NON-NLS-1$
@@ -197,7 +256,11 @@
Set<TreeModelEvent> events = createTreeEvents(e);
for (TreeModelEvent evt : events) {
- fireTreeNodesInserted(evt);
+ if (transactionCount > 0) {
+ allEvents.add(new TreeEventWithType(evt,
EventType.INSERT));
+ } else {
+ fireTreeNodesInserted(evt);
+ }
}
if (e.getChild() instanceof SQLTable &&
foldersInTables.get(e.getChild()) == null) {
@@ -208,8 +271,13 @@
for (int i = 0; i < folderList.size(); i++) {
positions[i] = i;
}
- fireTreeNodesInserted(new TreeModelEvent(table,
getPathToNode(table),
- positions, folderList.toArray()));
+ final TreeModelEvent evt = new TreeModelEvent(table,
getPathToNode(table),
+ positions, folderList.toArray());
+ if (transactionCount > 0) {
+ allEvents.add(new TreeEventWithType(evt,
EventType.INSERT));
+ } else {
+ fireTreeNodesInserted(evt);
+ }
} else {
setupTreeForNode((SQLObject) e.getChild());
}
@@ -217,6 +285,9 @@
}
public void childRemoved(SPChildEvent e) {
+ if (!root.getSession().isForegroundThread())
+ throw new IllegalStateException("Removing a child " +
e.getChild() + " to " + e.getSource() +
+ " not on the foreground thread.");
if (logger.isDebugEnabled()) {
logger.debug("dbchildrenremoved. source="+e.getSource()
//$NON-NLS-1$
+" index: "+e.getIndex() //$NON-NLS-1$
@@ -230,23 +301,58 @@
Set<TreeModelEvent> events = createTreeEvents(e);
for (TreeModelEvent evt : events) {
- fireTreeNodesRemoved(evt);
+ if (transactionCount > 0) {
+ allEvents.add(new TreeEventWithType(evt,
EventType.REMOVE));
+ } else {
+ fireTreeNodesRemoved(evt);
+ }
}
}
public void transactionEnded(TransactionEvent e) {
- //do nothing
+ if (!root.getSession().isForegroundThread())
+ throw new IllegalStateException("Transaction ended for " +
e.getSource() +
+ " while not on the foreground thread.");
+ if (transactionCount == 0) {
+ throw new IllegalStateException("Transaction ended outside
of a transaction.");
+ }
+ transactionCount--;
+ if (transactionCount == 0) {
+ List<TreeEventWithType> currentEvents = new
ArrayList<TreeEventWithType>(allEvents);
+ for (TreeEventWithType evt : currentEvents) {
+ if (evt.getType() == EventType.INSERT) {
+ fireTreeNodesInserted(evt.getEvt());
+ } else if (evt.getType() == EventType.REMOVE) {
+ fireTreeNodesRemoved(evt.getEvt());
+ } else if (evt.getType() == EventType.CHANGE) {
+ fireTreeNodesChanged(evt.getEvt());
+ } else {
+ throw new IllegalStateException("Unknown event
type " + evt.getType());
+ }
+ }
+ allEvents.clear();
+ }
}
public void transactionRollback(TransactionEvent e) {
- //do nothing
+ if (!root.getSession().isForegroundThread())
+ throw new IllegalStateException("Transaction rolled back
for " + e.getSource() +
+ " while not on the foreground thread.");
+ transactionCount = 0;
+ allEvents.clear();
}
public void transactionStarted(TransactionEvent e) {
- //do nothing
+ if (!root.getSession().isForegroundThread())
+ throw new IllegalStateException("Transaction started for "
+ e.getSource() +
+ " while not on the foreground thread.");
+ transactionCount++;
}
public void propertyChanged(PropertyChangeEvent e) {
+ if (!root.getSession().isForegroundThread())
+ throw new IllegalStateException("Changing the property" +
e.getPropertyName() + " on " + e.getSource() +
+ " not on the foreground thread.");
logger.debug("dbObjectChanged. source="+e.getSource());
//$NON-NLS-1$
if ((!SwingUtilities.isEventDispatchThread()) &&
(!refireOnAnyThread)) {
logger.warn("Not refiring because this is not the EDT. You
will need to call refreshTreeStructure() at some point in the future.");
//$NON-NLS-1$
@@ -291,7 +397,12 @@
refreshTreeStructure();
logger.info("Changing a UUID. This should only be done
during load.");
} else {
- fireTreeNodesChanged(new TreeModelEvent(this,
getPathToNode(source)));
+ final TreeModelEvent evt = new TreeModelEvent(this,
getPathToNode(source));
+ if (transactionCount > 0) {
+ allEvents.add(new TreeEventWithType(evt,
EventType.CHANGE));
+ } else {
+ fireTreeNodesChanged(evt);
+ }
}
}
@@ -382,7 +493,7 @@
SQLObject sqlParent = (SQLObject) parent;
try {
- if (logger.isDebugEnabled())
logger.debug("returning "+sqlParent.getChildrenWithoutPopulating().size());
//$NON-NLS-1$
+ if (logger.isDebugEnabled())
logger.debug("returning "+sqlParent.getChildren().size()); //$NON-NLS-1$
return sqlParent.getChildren().size();
} catch (Exception e) {
throw new RuntimeException(e);
@@ -402,16 +513,17 @@
}
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$
+ SPObject spChild = (SPObject) child;
+ if (logger.isDebugEnabled())
logger.debug("DBTreeModel.getIndexOfChild("+parent+","+child+"):
returning "+((SQLObject)
parent).getChildren(spChild.getClass()).indexOf(child)); //$NON-NLS-1$
//$NON-NLS-2$ //$NON-NLS-3$
if (parent instanceof FolderNode) {
- return ((FolderNode) parent).getChildren().indexOf(child);
+ return ((FolderNode)
parent).getChildren(spChild.getClass()).indexOf(child);
} else if (parent instanceof SQLTable) {
if (foldersInTables.get((SQLTable) parent) == null) return -1;
return foldersInTables.get((SQLTable) parent).indexOf(child);
}
- return ((SQLObject)
parent).getChildrenWithoutPopulating().indexOf(child);
+ return ((SQLObject)
parent).getChildren(spChild.getClass()).indexOf(child);
}
// -------------- treeModel event source support -----------------