001: /*
002: * TableDependency.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.db;
013:
014: import java.util.ArrayList;
015: import java.util.List;
016: import workbench.log.LogMgr;
017: import workbench.storage.DataStore;
018:
019: /**
020: *
021: * @author support@sql-workbench.net
022: */
023: public class TableDependency {
024: private WbConnection connection;
025: private TableIdentifier theTable;
026: private DependencyNode tableRoot;
027: private DbMetadata wbMetadata;
028: private ArrayList<DependencyNode> leafs;
029: private boolean directChildren = false;
030: private boolean readAborted = false;
031:
032: public TableDependency(WbConnection con, TableIdentifier tbl) {
033: this .connection = con;
034: this .wbMetadata = this .connection.getMetadata();
035: this .theTable = this .wbMetadata.findTable(tbl);
036: }
037:
038: public void setRetrieveDirectChildrenOnly(boolean flag) {
039: this .directChildren = flag;
040: }
041:
042: public DependencyNode findLeafNodeForTable(TableIdentifier table) {
043: String findExpr = table.getTableExpression(connection);
044: for (DependencyNode node : leafs) {
045: String expr = node.getTable()
046: .getTableExpression(connection);
047: if (expr.equalsIgnoreCase(findExpr))
048: return node;
049: }
050: return null;
051: }
052:
053: public void readTreeForChildren() {
054: readDependencyTree(true);
055: }
056:
057: public void readTreeForParents() {
058: readDependencyTree(false);
059: }
060:
061: public void readDependencyTree(boolean exportedKeys) {
062: if (this .theTable == null)
063: return;
064: if (this .connection == null)
065: return;
066: this .readAborted = false;
067: this .leafs = new ArrayList<DependencyNode>();
068:
069: // Make sure we are using the "correct" TableIdentifier
070: // if the TableIdentifier passed in the constructor was
071: // created "on the commandline" e.g. by using a user-supplied
072: // table name, we might not correctly find or compare all nodes
073: // those identifiers will not have the flag "neverAdjustCase" set
074: TableIdentifier tableToUse = this .theTable;
075: if (!this .theTable.getNeverAdjustCase()) {
076: tableToUse = this .wbMetadata.findTable(theTable);
077: }
078: if (tableToUse == null)
079: return;
080: this .tableRoot = new DependencyNode(tableToUse);
081: //this.currentLevel = 0;
082: this .readTree(this .tableRoot, exportedKeys, 0);
083: }
084:
085: /**
086: * Create the dependency tree.
087: */
088: private int readTree(DependencyNode parent, boolean exportedKeys,
089: int level) {
090: try {
091: DataStore ds = null;
092: int catalogcol;
093: int schemacol;
094: int tablecol;
095: int fknamecol;
096: int tablecolumncol;
097: int parentcolumncol;
098:
099: TableIdentifier ptbl = this .wbMetadata
100: .resolveSynonym(parent.getTable());
101:
102: if (exportedKeys) {
103: catalogcol = 4;
104: schemacol = 5;
105: tablecol = 6;
106: fknamecol = 11;
107: tablecolumncol = 7;
108: parentcolumncol = 3;
109: ds = this .wbMetadata.getExportedKeys(ptbl);
110: } else {
111: catalogcol = 0;
112: schemacol = 1;
113: tablecol = 2;
114: fknamecol = 11;
115: tablecolumncol = 3;
116: parentcolumncol = 7;
117: ds = this .wbMetadata.getImportedKeys(ptbl);
118: }
119:
120: int count = ds.getRowCount();
121:
122: for (int i = 0; i < count; i++) {
123: String catalog = ds.getValueAsString(i, catalogcol);
124: String schema = ds.getValueAsString(i, schemacol);
125: String table = ds.getValueAsString(i, tablecol);
126: String fkname = ds.getValueAsString(i, fknamecol);
127:
128: TableIdentifier tbl = new TableIdentifier(catalog,
129: schema, table);
130:
131: tbl.setNeverAdjustCase(true);
132: DependencyNode child = parent.addChild(tbl, fkname);
133: String tablecolumn = ds.getValueAsString(i,
134: tablecolumncol); // the column in "table" referencing the other table
135: String parentcolumn = ds.getValueAsString(i,
136: parentcolumncol); // the column in the parent table
137:
138: int update = ds.getValueAsInt(i, 9, -1);
139: int delete = ds.getValueAsInt(i, 10, -1);
140: child.setUpdateAction(this .wbMetadata.getDbSettings()
141: .getRuleDisplay(update));
142: child.setDeleteAction(this .wbMetadata.getDbSettings()
143: .getRuleDisplay(delete));
144: child.addColumnDefinition(tablecolumn, parentcolumn);
145: }
146:
147: //level ++;
148: if (level > 15) {
149: // this is a bit paranoid, as I am testing for cycles before recursing
150: // into the next child. This is a safetey net, just in case the cycle
151: // is not detected. Better display the user incorrect data, than
152: // ending up in an endless loop.
153: // A circular dependency with more than 10 levels is an ugly design anyway :)
154: LogMgr
155: .logError(
156: "TableDependency.readDependencyTree()",
157: "Endless reference cycle detected for root="
158: + this .tableRoot + ", parent="
159: + parent, null);
160: this .readAborted = true;
161: return count;
162: }
163:
164: if (directChildren && level == 1)
165: return count;
166:
167: List<DependencyNode> children = parent.getChildren();
168: for (DependencyNode child : children) {
169: String s = child.getTable().getTableName();
170: if (!isCycle(child, parent)) {
171: this .readTree(child, exportedKeys, level + 1);
172: }
173: this .leafs.add(child);
174: }
175: return count;
176: } catch (Exception e) {
177: LogMgr.logError("TableDependencyTree.readTree()",
178: "Error when reading FK definition", e);
179: }
180: return 0;
181: }
182:
183: private boolean isCycle(DependencyNode child, DependencyNode parent) {
184: if (child.equals(parent))
185: return true;
186: if (child.getTable().equals(parent.getTable()))
187: return true;
188:
189: DependencyNode nextParent = parent.getParent();
190: while (nextParent != null) {
191: if (child.equals(nextParent))
192: return true;
193: nextParent = nextParent.getParent();
194: }
195: return false;
196: }
197:
198: public boolean wasAborted() {
199: return this .readAborted;
200: }
201:
202: public List<DependencyNode> getLeafs() {
203: return this .leafs;
204: }
205:
206: public DependencyNode getRootNode() {
207: return this.tableRoot;
208: }
209:
210: }
|