001: /*
002: * ReferenceTableNavigator.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.gui.sql;
013:
014: import java.awt.event.ActionEvent;
015: import java.awt.event.ActionListener;
016: import java.beans.PropertyChangeEvent;
017: import java.beans.PropertyChangeListener;
018: import java.beans.PropertyChangeListener;
019: import java.util.Collection;
020: import java.util.EventObject;
021: import java.util.LinkedList;
022: import java.util.List;
023: import java.util.Map;
024: import javax.swing.JMenuItem;
025: import javax.swing.JPopupMenu;
026: import javax.swing.event.ListSelectionEvent;
027: import javax.swing.event.ListSelectionListener;
028: import javax.swing.event.ListSelectionListener;
029: import javax.swing.event.MenuEvent;
030: import javax.swing.event.MenuListener;
031: import javax.swing.event.MenuListener;
032: import javax.swing.event.PopupMenuEvent;
033: import javax.swing.event.PopupMenuListener;
034: import javax.swing.event.PopupMenuListener;
035: import workbench.db.ColumnIdentifier;
036: import workbench.db.DependencyNode;
037: import workbench.db.ReferenceTableNavigation;
038: import workbench.db.TableDependency;
039: import workbench.db.TableIdentifier;
040: import workbench.db.WbConnection;
041: import workbench.gui.MainWindow;
042: import workbench.gui.WbSwingUtilities;
043: import workbench.gui.actions.WbAction;
044: import workbench.gui.components.WbMenu;
045: import workbench.gui.components.WbTable;
046: import workbench.gui.dbobjects.EditorTabSelectMenu;
047: import workbench.log.LogMgr;
048: import workbench.resource.ResourceMgr;
049: import workbench.storage.ColumnData;
050: import workbench.storage.DataStore;
051: import workbench.util.ExceptionUtil;
052: import workbench.util.StringUtil;
053: import workbench.util.WbThread;
054:
055: /**
056: * A class to manage the popup menu for the reference table navigation.
057: * This class populates the popup menu according to the passed in base table
058: * to generate the SELECT statements for retrieving referenced database rows.
059: *
060: * It uses several other classes to build the GUI and retrieve the necessary
061: * database information
062: * @see workbench.db.ReferenceTableNavigation
063: * @see workbench.db.TableDependency
064: * @see workbench.gui.dbobjects.EditorTabSelectMenu
065: * @author support@sql-workbench.net
066: */
067: public class ReferenceTableNavigator implements ListSelectionListener,
068: ActionListener, PopupMenuListener, MenuListener,
069: PropertyChangeListener {
070: private MainWindow container;
071: private WbTable source;
072: private WbMenu selectParentTables;
073: private WbMenu selectChildTables;
074: private boolean parentMenuInitialized = false;
075: private boolean childMenuInitialized = false;
076: private TableIdentifier baseTable;
077: private ReferenceTableNavigation parentNavigation;
078: private ReferenceTableNavigation childNavigation;
079:
080: public ReferenceTableNavigator(DwPanel data, MainWindow win) {
081: this .setSourceTable(data.getTable());
082: data.addPropertyChangeListener(DwPanel.PROP_UPDATE_TABLE, this );
083: this .container = win;
084: rebuildMenu();
085: }
086:
087: private void setSourceTable(WbTable data) {
088: if (this .source != null) {
089: this .source.getSelectionModel()
090: .removeListSelectionListener(this );
091: }
092: this .source = data;
093: this .source.getSelectionModel().addListSelectionListener(this );
094:
095: selectParentTables = new WbMenu(ResourceMgr
096: .getString("MnuTxtReferencedRows"));
097: selectParentTables.addMenuListener(this );
098: selectParentTables.getPopupMenu().addPopupMenuListener(this );
099: selectParentTables.setEnabled(false);
100: parentMenuInitialized = false;
101:
102: selectChildTables = new WbMenu(ResourceMgr
103: .getString("MnuTxtReferencingRows"));
104: selectChildTables.addMenuListener(this );
105: selectChildTables.getPopupMenu().addPopupMenuListener(this );
106: selectChildTables.setEnabled(false);
107: childMenuInitialized = false;
108:
109: this .source.addPopupMenu(selectParentTables, true);
110: this .source.addPopupMenu(selectChildTables, false);
111: }
112:
113: public void valueChanged(ListSelectionEvent evt) {
114: boolean selected = this .source.getSelectedRowCount() > 0;
115: selectParentTables.setEnabled(selected);
116: selectChildTables.setEnabled(selected);
117: }
118:
119: private TableIdentifier getUpdateTable() {
120: if (this .baseTable != null)
121: return this .baseTable;
122: TableIdentifier tbl = this .source.getDataStore()
123: .getUpdateTable();
124: if (tbl == null) {
125: if (this .source.getDataStore().checkUpdateTable()) {
126: tbl = this .source.getDataStore().getUpdateTable();
127: }
128: }
129: if (tbl != null) {
130: this .baseTable = tbl.createCopy();
131: if (this .baseTable.getSchema() == null) {
132: String schema = this .getConnection().getMetadata()
133: .getSchemaToUse();
134: this .baseTable.setSchema(schema);
135: }
136: }
137: return this .baseTable;
138: }
139:
140: public void menuSelected(MenuEvent evt) {
141: }
142:
143: public void menuDeselected(MenuEvent evt) {
144: closePopup(evt);
145: }
146:
147: public void menuCanceled(MenuEvent evt) {
148: selectParentTables.getPopupMenu().setVisible(false);
149: selectChildTables.getPopupMenu().setVisible(false);
150: }
151:
152: public void popupMenuWillBecomeVisible(PopupMenuEvent evt) {
153: JPopupMenu pop = (JPopupMenu) evt.getSource();
154: if (pop == this .selectParentTables.getPopupMenu()
155: && !parentMenuInitialized) {
156: buildParentTablesMenu();
157: }
158: if (pop == this .selectChildTables.getPopupMenu()
159: && !childMenuInitialized) {
160: buildChildTablesMenu();
161: }
162: }
163:
164: public void popupMenuWillBecomeInvisible(PopupMenuEvent evt) {
165:
166: }
167:
168: public void popupMenuCanceled(PopupMenuEvent evt) {
169: closePopup(evt);
170: }
171:
172: private void closePopup(EventObject evt) {
173: if (evt.getSource() == this .selectParentTables.getPopupMenu()) {
174: selectParentTables.getPopupMenu().setVisible(false);
175: }
176: if (evt.getSource() == this .selectChildTables.getPopupMenu()) {
177: selectChildTables.getPopupMenu().setVisible(false);
178: }
179: }
180:
181: private WbConnection getConnection() {
182: return this .source.getDataStore().getOriginalConnection();
183: }
184:
185: private JMenuItem createLoadingItem() {
186: JMenuItem item = new JMenuItem(ResourceMgr
187: .getString("MsgLoadDependency"));
188: item.setVisible(true);
189: item.setEnabled(false);
190: return item;
191: }
192:
193: private void rebuildMenu() {
194: synchronized (selectParentTables) {
195: selectParentTables.removeAll();
196: selectParentTables.add(createLoadingItem());
197: parentMenuInitialized = false;
198: }
199:
200: synchronized (selectChildTables) {
201: selectChildTables.removeAll();
202: selectChildTables.add(createLoadingItem());
203: childMenuInitialized = false;
204: }
205: }
206:
207: private void buildChildTablesMenu() {
208: WbThread init = new WbThread("InitChildren") {
209: public void run() {
210: synchronized (getConnection()) {
211: childNavigation = new ReferenceTableNavigation(
212: getUpdateTable(), getConnection());
213: childNavigation.readTreeForChildren();
214: }
215: synchronized (childNavigation) {
216: buildMenu(selectChildTables, childNavigation,
217: "select-child");
218: childMenuInitialized = true;
219: }
220: }
221: };
222: init.start();
223: }
224:
225: private void buildParentTablesMenu() {
226: WbThread init = new WbThread("InitParents") {
227: public void run() {
228: synchronized (getConnection()) {
229: parentNavigation = new ReferenceTableNavigation(
230: getUpdateTable(), getConnection());
231: parentNavigation.readTreeForParents();
232: }
233: synchronized (parentNavigation) {
234: buildMenu(selectParentTables, parentNavigation,
235: "select-parent");
236: parentMenuInitialized = true;
237: }
238: }
239: };
240: init.start();
241: }
242:
243: private void buildMenu(WbMenu menu, ReferenceTableNavigation navi,
244: String cmd) {
245: List<JMenuItem> itemsToAdd = new LinkedList<JMenuItem>();
246:
247: TableIdentifier tbl = getUpdateTable();
248: if (tbl != null) {
249: TableDependency dep = navi.getTree();
250: List<DependencyNode> tables = dep.getLeafs();
251: WbConnection con = getConnection();
252:
253: if (tables == null || tables.size() == 0) {
254: JMenuItem item = new JMenuItem(ResourceMgr
255: .getString("MnuTxtNoTables"));
256: item.setEnabled(false);
257: item.setVisible(true);
258: itemsToAdd.add(item);
259: } else {
260: for (DependencyNode node : tables) {
261: JMenuItem item = null;
262: Collection<String> cols = node.getColumns()
263: .keySet();
264: StringBuilder display = new StringBuilder(cols
265: .size() * 10);
266: display.append(node.getTable().getTableExpression(
267: con));
268: display.append(" (");
269: int index = 0;
270: for (String col : cols) {
271: if (index > 0)
272: display.append(',');
273: display.append(col);
274: index++;
275: }
276: display.append(')');
277: item = new EditorTabSelectMenu(this , display
278: .toString(), "LblShowDataInNewTab",
279: "MsgRelatedTabHint", container);
280: item.setVisible(true);
281: boolean hasColumns = hasColumns(node);
282: item.setEnabled(hasColumns);
283: if (!hasColumns) {
284: item.setToolTipText(ResourceMgr
285: .getString("MsgRelatedNoColumns"));
286: }
287: item.setActionCommand(cmd);
288:
289: itemsToAdd.add(item);
290: }
291: }
292: } else {
293: JMenuItem item = new JMenuItem(ResourceMgr
294: .getString("MnuTxtNoUpdTbl"));
295: item.setEnabled(false);
296: itemsToAdd.add(item);
297: }
298: addMenuItems(menu, itemsToAdd);
299: }
300:
301: /**
302: * Check if our source DataStore has all necessary columns to
303: * be able to retrieve the table referenced by the given DependencyNode
304: */
305: private boolean hasColumns(DependencyNode node) {
306: Map<String, String> cols = node.getColumns();
307: DataStore ds = this .source.getDataStore();
308: for (String col : cols.values()) {
309: if (ds.getColumnIndex(col) == -1)
310: return false;
311: }
312: return true;
313: }
314:
315: private void addMenuItems(final WbMenu menu,
316: final List<JMenuItem> items) {
317: WbSwingUtilities.invoke(new Runnable() {
318: public void run() {
319: synchronized (menu) {
320: menu.removeAll();
321: for (JMenuItem item : items) {
322: menu.add(item);
323: }
324: JPopupMenu pop = menu.getPopupMenu();
325: if (pop.isVisible()) {
326: pop.invalidate();
327: // The popup menu is not repainted correctly
328: // if not made invisible. Neither doLayout() or updateUI()
329: // adjust the height and width of the popup menu correctly
330: pop.setVisible(false);
331: pop.setVisible(true);
332: }
333: }
334: }
335: });
336: }
337:
338: private List<List<ColumnData>> getColumnData(DependencyNode node) {
339: List<List<ColumnData>> rows = new LinkedList<List<ColumnData>>();
340: int[] selectedRows = this .source.getSelectedRows();
341: int rowCount = selectedRows.length;
342:
343: Map<String, String> columns = node.getColumns();
344: DataStore ds = this .source.getDataStore();
345: for (int i = 0; i < rowCount; i++) {
346: List<ColumnData> rowData = new LinkedList<ColumnData>();
347: //for (Map.Entry<String, String> entry : columns.entrySet())
348: for (String col : columns.values()) {
349: int colIndex = ds.getColumnIndex(col);
350: if (colIndex > -1) {
351: Object data = ds
352: .getValue(selectedRows[i], colIndex);
353: ColumnData cdata = new ColumnData(data,
354: new ColumnIdentifier(col));
355: rowData.add(cdata);
356: }
357: }
358: if (rowData.size() > 0) {
359: rows.add(rowData);
360: }
361: }
362:
363: return rows;
364: }
365:
366: public void actionPerformed(ActionEvent evt) {
367: JMenuItem item = (JMenuItem) evt.getSource();
368: String cmd = item.getActionCommand();
369: int containerIndex = -42;
370:
371: if (cmd.startsWith(EditorTabSelectMenu.PANEL_CMD_PREFIX)) {
372: containerIndex = StringUtil.getIntValue(cmd
373: .substring(EditorTabSelectMenu.PANEL_CMD_PREFIX
374: .length()), -1);
375: JPopupMenu popup = (JPopupMenu) item.getParent();
376: item = (JMenuItem) popup.getInvoker();
377: cmd = item.getActionCommand();
378: }
379:
380: WbConnection con = getConnection();
381: TableIdentifier tbl = new TableIdentifier(item.getText());
382:
383: ReferenceTableNavigation navi = null;
384: String sql = null;
385: String error = null;
386: List<List<ColumnData>> rowData = null;
387: try {
388: if ("select-child".equals(cmd)) {
389: DependencyNode node = this .childNavigation
390: .getNodeForTable(tbl);
391: if (node == null) {
392: error = "Could not find child table from menu item!";
393: LogMgr.logError(
394: "ReferenceTableNavigator.actionPerformed",
395: error, null);
396: } else {
397: rowData = getColumnData(node);
398: sql = this .childNavigation.getSelectForChild(tbl,
399: rowData);
400: }
401: } else if ("select-parent".equals(cmd)) {
402: DependencyNode node = this .parentNavigation
403: .getNodeForTable(tbl);
404: if (node == null) {
405: error = "Could not find parent table from menu item!";
406: LogMgr.logError(
407: "ReferenceTableNavigator.actionPerformed",
408: error, null);
409: } else {
410: rowData = getColumnData(node);
411: sql = this .parentNavigation.getSelectForParent(tbl,
412: rowData);
413: }
414: }
415: } catch (Exception e) {
416: LogMgr.logError("ReferenceTableNavigator.actionPerformed",
417: "Error when creating SQL", e);
418: error = ExceptionUtil.getDisplay(e);
419: }
420: if (sql == null || error != null) {
421: WbSwingUtilities.showErrorMessage(this .container, error);
422: return;
423: }
424:
425: String comment = comment = ResourceMgr
426: .getString("MsgLoadRelatedComment")
427: + " " + getUpdateTable().getTableExpression(con);
428:
429: boolean logText = WbAction.isCtrlPressed(evt);
430: // boolean shiftPressed = WbAction.isShiftPressed(evt);
431: // if (receiver != null)
432: // {
433: // if (this.container != null && ctrlPressed)
434: // {
435: // PanelContentSender sender = new PanelContentSender(container);
436: // sender.showResult(sql, comment, PanelContentSender.NEW_PANEL);
437: // }
438: // else
439: // {
440: // ResultReceiver.ShowType how = (shiftPressed ? ResultReceiver.ShowType.appendText : ResultReceiver.ShowType.logText);
441: // receiver.showResult(sql, comment, how);
442: // }
443: // }
444: if (this .container != null) {
445: PanelContentSender sender = new PanelContentSender(
446: container);
447: // showLog will only be evaluated for existing tabs
448: // if the target tab is the current tab, show
449: sender.showResult(sql, comment, containerIndex, logText);
450: }
451: }
452:
453: /**
454: * Reset the internal dependency tree and popup menu.
455: * The next time the menu should be displayed, the
456: * source table will then abe queried for the new
457: * update table.
458: */
459: public void reset() {
460: this .baseTable = null;
461: this .rebuildMenu();
462: }
463:
464: public void propertyChange(PropertyChangeEvent evt) {
465: if (evt.getPropertyName().equals(DwPanel.PROP_UPDATE_TABLE)) {
466: reset();
467: }
468: }
469:
470: }
|