Revision: 3241
Author: silva.josemanuel1
Date: Thu Jan 21 12:13:16 2010
Log: Ensured ETL lineage is preserved when copying/pasting in cases where
it wasn't. Also created a prompt for the user in case ETL lineage could not
be preserved, so that the user may be notified and continue or cancel the
operation.
FIXED - bug 1997: Copy tables causes exception
http://trillian.sqlpower.ca/bugzilla/show_bug.cgi?id=1997
http://code.google.com/p/power-architect/source/detail?r=3241
Added:
/trunk/src/ca/sqlpower/architect/swingui/ImportSafetyChecker.java
Modified:
/trunk/src/ca/sqlpower/architect/swingui/ASUtils.java
/trunk/src/ca/sqlpower/architect/swingui/PlayPen.java
=======================================
--- /dev/null
+++ /trunk/src/ca/sqlpower/architect/swingui/ImportSafetyChecker.java Thu
Jan 21 12:13:16 2010
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2010, SQL Power Group Inc.
+ *
+ * This file is part of Power*Architect.
+ *
+ * Power*Architect is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Power*Architect is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package ca.sqlpower.architect.swingui;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import ca.sqlpower.object.SPObject;
+import ca.sqlpower.object.SPObjectUtils;
+import ca.sqlpower.sqlobject.SQLColumn;
+import ca.sqlpower.sqlobject.SQLDatabase;
+import ca.sqlpower.sqlobject.SQLObject;
+import ca.sqlpower.util.UserPrompter;
+import ca.sqlpower.util.UserPrompter.UserPromptOptions;
+import ca.sqlpower.util.UserPrompter.UserPromptResponse;
+import ca.sqlpower.util.UserPrompterFactory.UserPromptType;
+
+/**
+ *
+ * Used to filter SQLObjects from an import operation. It was originally
made to determine
+ * if a copy and paste operation between different sessions would result
+ * in losing ETL lineage, and notifies the user giving them the option
+ * to continue the operation or not. However, it is hopefully designed in
such
+ * a way that other filters could be easily added or implemented.
+ *
+ */
+
+class ImportSafetyChecker {
+
+ /**
+ * The session having objects imported into it.
+ */
+ private final ArchitectSwingSession targetSession;
+
+ /**
+ * Set true by the user prompt if the whole import is to be cancelled
(everything will be filtered).
+ */
+ private boolean isCancelled = false;
+
+ /**
+ * Used to prompt the user when ETL lineage will be lost as a result
of copying across sessions.
+ */
+ UserPrompter loseLineage;
+
+ ImportSafetyChecker(ArchitectSwingSession session) {
+
+ this.targetSession = session;
+
+ loseLineage = targetSession.createUserPrompter(
+ "One or more columns in {0} have ETL lineage from their
source session. " +
+ "\nThis lineage will not be able to be copied along with
them. " +
+ "\nCopy {0} anyway, and lose ETL lineage of one or more
columns?",
+ UserPromptType.BOOLEAN, UserPromptOptions.OK_NOTOK_CANCEL,
+ UserPromptResponse.NOT_OK,
UserPromptResponse.NOT_OK, "Yes", "No", "Cancel");
+
+ }
+
+ List<SQLObject> filterImportedItems(Collection<SQLObject> items) {
+ List<SQLObject> acceptedItems = new ArrayList<SQLObject>();
+
+ for (SQLObject item : items) {
+
+ if (accept(item)) {
+ acceptedItems.add(item);
+ }
+
+ if (isCancelled) {
+ return Collections.emptyList();
+ }
+
+ }
+
+ return acceptedItems;
+ }
+
+ /**
+ * Checks if this item should not be filtered out from the import
operation.
+ */
+ private boolean accept(SQLObject item) {
+
+ SPObject parent = item;
+
+ while (parent.getParent() != null) {
+ parent = parent.getParent();
+ }
+
+ // Accept the item if it is in the same project/session, since ETL
lineage won't be a problem.
+ if (targetSession.getWorkspace() == parent) {
+ return true;
+ }
+ if (!visit(item)) {
+ UserPromptResponse response =
loseLineage.promptUser(item.getPhysicalName());
+ if (response == UserPromptResponse.CANCEL) {
+ isCancelled = true;
+ }
+ return (response == UserPromptResponse.OK);
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Checks if this item and its entire subtree pass the filter.
+ */
+ public boolean visit(SQLObject item) {
+
+ boolean subtreeAccepted = true;
+
+ if (item instanceof SQLColumn) {
+ return sourceDatabaseAccessible((SQLColumn) item,
targetSession);
+ }
+
+ for (SQLObject child : item.getChildren()) {
+ if (!(subtreeAccepted &= visit(child))) {
+ break;
+ }
+ }
+ return subtreeAccepted;
+ }
+
+ /**
+ * Checks if the given sourceColumn's source database is accessible
from the targetSession.
+ * If not, ETL lineage is impossible to preserve.
+ */
+ private boolean sourceDatabaseAccessible(SQLColumn sourceColumn,
ArchitectSwingSession targetSession) {
+
+ SQLDatabase sourceSourceDatabase;
+
+ // The source column could be its own source if is is being
+ // copied directly from a database, not a play pen.
+ if (sourceColumn.getSourceColumn() == null) {
+ sourceColumn.setSourceColumn(sourceColumn);
+ }
+ sourceSourceDatabase =
SPObjectUtils.getAncestor(sourceColumn.getSourceColumn(),
SQLDatabase.class);
+
+ if (targetSession.getSourceDatabases().getDuplicateDbcs(
+ sourceSourceDatabase.getDataSource()) == null) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+}
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/ASUtils.java Fri Jan 15
15:02:04 2010
+++ /trunk/src/ca/sqlpower/architect/swingui/ASUtils.java Thu Jan 21
12:13:16 2010
@@ -526,12 +526,15 @@
);
}
- if (containingSession != currentSession) { //Duplicating a
SQLObject across sessions in the same context.
+ if (containingSession != currentSession) { //Duplicating a
SQLObject across sessions in the same context.
+ ImportSafetyChecker checker = new
ImportSafetyChecker(currentSession);
+ boolean preserveColumnSource = checker.visit(source); // check
if the columns can be preserved
+
return new DuplicateProperties(
true, //Have to add the source db to the project if
it's missing.
SQLTable.TransferStyles.COPY,
false,
- true,
+ preserveColumnSource,
true
);
}
@@ -571,6 +574,10 @@
if (duplicateProperties.getDefaultTransferStyle() ==
TransferStyles.REVERSE_ENGINEER) {
sourceColumn = source;
} else if (duplicateProperties.getDefaultTransferStyle() ==
TransferStyles.COPY) {
+ // getSourceColumn may return null if source is its own source
column.
+ if (source.getSourceColumn() == null) {
+ source.setSourceColumn(source);
+ }
sourceColumn = source.getSourceColumn();
} else {
sourceColumn = null;
@@ -606,6 +613,22 @@
List<SQLObject> ancestors =
SQLObjectUtils.ancestorList(sourceColumn);
System.out.println(ancestors);
SQLObject child = targetSourceDatabase;
+
+ // It seems sometimes the PokeDBTree takes too long to
populate the database,
+ // so this makes sure the new database is ready.
+ int waits = 0;
+ while (child.getChildren().size() == 0) {
+ try {
+ waits++;
+ Thread.sleep(50);
+ if (waits > 100) {
+ throw new RuntimeException("Waiting too long for
database to be populated.");
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
for (int i = 2; i < ancestors.size(); i++) {
SQLObject ancestor = ancestors.get(i);
System.out.println("Child " + child + " ancestor " +
ancestor);
=======================================
--- /trunk/src/ca/sqlpower/architect/swingui/PlayPen.java Mon Dec 21
08:27:43 2009
+++ /trunk/src/ca/sqlpower/architect/swingui/PlayPen.java Thu Jan 21
12:13:16 2010
@@ -1530,12 +1530,15 @@
errorMessage, getDoStuffException());
if (getNextProcess() != null) {
setCancelled(true);
-
}
}
- session.getPlayPen().startCompoundEdit("Drag to
Playpen"); //$NON-NLS-1$
-
+ session.getPlayPen().startCompoundEdit("Drag to
Playpen"); //$NON-NLS-1$
+
+ // Filter out objects that would lose ETL lineage against the user's
will.
+ ImportSafetyChecker checker = new
ImportSafetyChecker(session);
+ sqlObjects = checker.filterImportedItems(sqlObjects);
+
try {
// reset iterator