001: /*
002: * ProcedureListPanel.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.dbobjects;
013:
014: import java.awt.BorderLayout;
015: import java.awt.Component;
016: import java.awt.Container;
017: import java.awt.EventQueue;
018: import java.sql.DatabaseMetaData;
019: import java.util.ArrayList;
020: import java.util.List;
021:
022: import javax.swing.JPanel;
023: import javax.swing.JScrollPane;
024: import javax.swing.JSplitPane;
025: import javax.swing.JTabbedPane;
026: import javax.swing.ListSelectionModel;
027: import javax.swing.border.EmptyBorder;
028: import javax.swing.event.ListSelectionEvent;
029: import javax.swing.event.ListSelectionListener;
030: import javax.swing.table.TableCellRenderer;
031:
032: import workbench.db.DbMetadata;
033: import workbench.db.ProcedureReader;
034: import workbench.db.WbConnection;
035: import workbench.gui.WbSwingUtilities;
036: import workbench.gui.actions.ReloadAction;
037: import workbench.gui.components.WbScrollPane;
038: import workbench.gui.components.WbSplitPane;
039: import workbench.gui.components.WbTable;
040: import workbench.gui.components.WbTraversalPolicy;
041: import workbench.gui.renderer.ProcStatusRenderer;
042: import workbench.interfaces.PropertyStorage;
043: import workbench.interfaces.Reloadable;
044: import workbench.log.LogMgr;
045: import workbench.resource.ResourceMgr;
046: import workbench.resource.Settings;
047: import workbench.util.StringUtil;
048: import javax.swing.JLabel;
049: import javax.swing.table.TableColumn;
050: import javax.swing.table.TableColumnModel;
051: import workbench.WbManager;
052: import workbench.db.DbObject;
053: import workbench.db.ProcedureDefinition;
054: import workbench.db.TableIdentifier;
055: import workbench.db.oracle.OraclePackageParser;
056: import workbench.gui.MainWindow;
057: import workbench.gui.actions.CompileDbObjectAction;
058: import workbench.gui.actions.DropDbObjectAction;
059: import workbench.gui.actions.ScriptDbObjectAction;
060: import workbench.gui.components.DataStoreTableModel;
061: import workbench.gui.components.QuickFilterPanel;
062: import workbench.gui.components.WbTabbedPane;
063: import workbench.gui.renderer.RendererFactory;
064: import workbench.interfaces.CriteriaPanel;
065: import workbench.storage.DataStore;
066: import workbench.util.WbWorkspace;
067:
068: /**
069: * @author support@sql-workbench.net
070: */
071: public class ProcedureListPanel extends JPanel implements
072: ListSelectionListener, Reloadable, DbObjectList {
073: private WbConnection dbConnection;
074: private JPanel listPanel;
075: private CriteriaPanel findPanel;
076: private WbTable procList;
077: private WbTable procColumns;
078: protected DbObjectSourcePanel source;
079: private JTabbedPane displayTab;
080: private WbSplitPane splitPane;
081: private String currentSchema;
082: private String currentCatalog;
083: private boolean shouldRetrieve;
084: private CompileDbObjectAction compileAction;
085:
086: private JLabel infoLabel;
087: private boolean isRetrieving;
088: protected ProcStatusRenderer statusRenderer;
089:
090: public ProcedureListPanel(MainWindow parent) throws Exception {
091: this .displayTab = new WbTabbedPane();
092: this .displayTab.setTabPlacement(JTabbedPane.BOTTOM);
093:
094: this .procColumns = new DbObjectTable();
095:
096: JScrollPane scroll = new WbScrollPane(this .procColumns);
097:
098: Reloadable sourceReload = new Reloadable() {
099: public void reload() {
100: if (dbConnection.isBusy())
101: return;
102: try {
103: dbConnection.setBusy(true);
104: retrieveCurrentProcedure();
105: } finally {
106: dbConnection.setBusy(false);
107: }
108: }
109: };
110:
111: source = new DbObjectSourcePanel(parent, sourceReload);
112: this .displayTab.add(ResourceMgr
113: .getString("TxtDbExplorerSource"), source);
114: this .displayTab.add(ResourceMgr
115: .getString("TxtDbExplorerTableDefinition"), scroll);
116:
117: this .listPanel = new JPanel();
118: this .statusRenderer = new ProcStatusRenderer();
119: this .procList = new DbObjectTable() {
120: public TableCellRenderer getCellRenderer(int row, int column) {
121: if (column == ProcedureReader.COLUMN_IDX_PROC_LIST_TYPE)
122: return statusRenderer;
123: return super .getCellRenderer(row, column);
124: }
125: };
126:
127: this .procList.getSelectionModel()
128: .addListSelectionListener(this );
129: this .procList.getSelectionModel().setSelectionMode(
130: ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
131:
132: String[] cols = new String[] { "PROCEDURE_NAME", "TYPE",
133: "CATALOG", "SCHEMA", "REMARKS" };
134: this .findPanel = new QuickFilterPanel(this .procList, cols,
135: false, "procedurelist");
136:
137: ReloadAction a = new ReloadAction(this );
138:
139: this .findPanel.addToToolbar(a, true, false);
140: a.getToolbarButton().setToolTipText(
141: ResourceMgr.getString("TxtRefreshProcedureList"));
142: this .listPanel.setLayout(new BorderLayout());
143: this .listPanel.add((JPanel) findPanel, BorderLayout.NORTH);
144:
145: this .splitPane = new WbSplitPane(JSplitPane.HORIZONTAL_SPLIT);
146: this .splitPane.setOneTouchExpandable(true);
147: this .splitPane.setDividerSize(6);
148: scroll = new WbScrollPane(this .procList);
149:
150: this .listPanel.add(scroll, BorderLayout.CENTER);
151:
152: this .infoLabel = new JLabel("");
153: EmptyBorder b = new EmptyBorder(1, 3, 0, 0);
154: this .infoLabel.setBorder(b);
155: this .listPanel.add(this .infoLabel, BorderLayout.SOUTH);
156:
157: this .splitPane.setLeftComponent(this .listPanel);
158: this .splitPane.setRightComponent(displayTab);
159: this .splitPane.setDividerBorder(WbSwingUtilities.EMPTY_BORDER);
160: this .setLayout(new BorderLayout());
161: this .add(splitPane, BorderLayout.CENTER);
162:
163: WbTraversalPolicy pol = new WbTraversalPolicy();
164: pol.setDefaultComponent((JPanel) findPanel);
165: pol.addComponent((JPanel) findPanel);
166: pol.addComponent(this .procList);
167: pol.addComponent(this .procColumns);
168: this .setFocusTraversalPolicy(pol);
169: this .reset();
170: this .extendPopupMenu();
171: }
172:
173: private void extendPopupMenu() {
174: ScriptDbObjectAction createScript = new ScriptDbObjectAction(
175: this , procList.getSelectionModel());
176: procList.addPopupAction(createScript, true);
177:
178: this .compileAction = new CompileDbObjectAction(this ,
179: this .procList.getSelectionModel());
180: procList.addPopupAction(compileAction, false);
181:
182: DropDbObjectAction dropAction = new DropDbObjectAction(this ,
183: procList.getSelectionModel(), this );
184: procList.addPopupAction(dropAction, false);
185: }
186:
187: public void disconnect() {
188: this .dbConnection = null;
189: this .reset();
190: }
191:
192: public void reset() {
193: this .procList.reset();
194: this .procColumns.reset();
195: this .source.setText("");
196: }
197:
198: public void setConnection(WbConnection aConnection) {
199: this .dbConnection = aConnection;
200: this .source.setDatabaseConnection(aConnection);
201: this .reset();
202: this .compileAction.setConnection(aConnection);
203: }
204:
205: public void setCatalogAndSchema(String aCatalog, String aSchema,
206: boolean retrieve) throws Exception {
207: this .currentSchema = aSchema;
208: this .currentCatalog = aCatalog;
209: if (this .isVisible() && retrieve) {
210: this .retrieve();
211: } else {
212: this .reset();
213: this .shouldRetrieve = true;
214: }
215: }
216:
217: public void panelSelected() {
218: retrieveIfNeeded();
219: }
220:
221: public void retrieveIfNeeded() {
222: if (this .shouldRetrieve)
223: this .retrieve();
224: }
225:
226: public void retrieve() {
227: if (this .isRetrieving)
228: return;
229: if (!WbSwingUtilities.checkConnection(this , this .dbConnection))
230: return;
231:
232: try {
233: this .reset();
234: this .dbConnection.setBusy(true);
235: this .isRetrieving = true;
236: DbMetadata meta = dbConnection.getMetadata();
237: WbSwingUtilities.showWaitCursorOnWindow(this );
238: DataStore ds = meta.getProcedures(currentCatalog,
239: currentSchema);
240: final DataStoreTableModel model = new DataStoreTableModel(
241: ds);
242:
243: WbSwingUtilities.invoke(new Runnable() {
244: public void run() {
245: int rows = model.getRowCount();
246: infoLabel.setText(rows
247: + " "
248: + ResourceMgr
249: .getString("TxtTableListObjects"));
250: procList.setModel(model, true);
251: procList.adjustOrOptimizeColumns();
252: }
253: });
254: shouldRetrieve = false;
255: } catch (OutOfMemoryError mem) {
256: WbManager.getInstance().showOutOfMemoryError();
257: } catch (Throwable e) {
258: LogMgr.logError("ProcedureListPanel.retrieve() thread",
259: "Could not retrieve procedure list", e);
260: } finally {
261: this .isRetrieving = false;
262: this .dbConnection.setBusy(false);
263: WbSwingUtilities.showDefaultCursorOnWindow(this );
264: }
265:
266: }
267:
268: public void setVisible(boolean aFlag) {
269: super .setVisible(aFlag);
270: if (aFlag && this .shouldRetrieve)
271: this .retrieve();
272: }
273:
274: private String getWorkspacePrefix(int index) {
275: return "dbexplorer" + index + ".procedurelist.";
276: }
277:
278: public void saveSettings() {
279: storeSettings(Settings.getInstance(), this .getClass().getName()
280: + ".");
281: findPanel.saveSettings();
282: }
283:
284: public void saveToWorkspace(WbWorkspace w, int index) {
285: String prefix = getWorkspacePrefix(index);
286: storeSettings(w.getSettings(), prefix);
287: findPanel.saveSettings(w.getSettings(), prefix);
288: }
289:
290: private void storeSettings(PropertyStorage props, String prefix) {
291: props.setProperty(prefix + "divider", this .splitPane
292: .getDividerLocation());
293: }
294:
295: public void restoreSettings() {
296: readSettings(Settings.getInstance(), this .getClass().getName()
297: + ".");
298: findPanel.restoreSettings();
299: }
300:
301: public void readFromWorkspace(WbWorkspace w, int index) {
302: String prefix = getWorkspacePrefix(index);
303: readSettings(w.getSettings(), prefix);
304: this .findPanel.restoreSettings(w.getSettings(), prefix);
305: }
306:
307: private void readSettings(PropertyStorage props, String prefix) {
308: int loc = props.getIntProperty(prefix + "divider", 200);
309: this .splitPane.setDividerLocation(loc);
310: }
311:
312: public void valueChanged(ListSelectionEvent e) {
313: if (e.getSource() != this .procList.getSelectionModel())
314: return;
315: if (e.getValueIsAdjusting())
316: return;
317: retrieveCurrentProcedure();
318: }
319:
320: protected void retrieveCurrentProcedure() {
321: int row = this .procList.getSelectedRow();
322:
323: if (row < 0)
324: return;
325:
326: final String proc = this .procList.getValueAsString(row,
327: ProcedureReader.COLUMN_IDX_PROC_LIST_NAME);
328: final String schema = this .procList.getValueAsString(row,
329: ProcedureReader.COLUMN_IDX_PROC_LIST_SCHEMA);
330: final String catalog = this .procList.getValueAsString(row,
331: ProcedureReader.COLUMN_IDX_PROC_LIST_CATALOG);
332: final int type = this .procList.getDataStore().getValueAsInt(
333: row, ProcedureReader.COLUMN_IDX_PROC_LIST_TYPE,
334: DatabaseMetaData.procedureResultUnknown);
335: EventQueue.invokeLater(new Runnable() {
336: public void run() {
337: retrieveProcDefinition(catalog, schema, proc, type);
338: }
339: });
340: }
341:
342: private void retrieveProcDefinition(String catalog, String schema,
343: String proc, int type) {
344: if (this .dbConnection == null)
345: return;
346: if (!WbSwingUtilities.checkConnection(this , this .dbConnection))
347: return;
348:
349: DbMetadata meta = dbConnection.getMetadata();
350: Container parent = this .getParent();
351: WbSwingUtilities.showWaitCursor(parent);
352: CharSequence sql = null;
353: try {
354: dbConnection.setBusy(true);
355: try {
356: DataStoreTableModel model = new DataStoreTableModel(
357: meta.getProcedureColumns(catalog, schema, proc));
358: procColumns.setModel(model, true);
359:
360: TableColumnModel colmod = procColumns.getColumnModel();
361: // Assign the correct renderer to display java.sql.Types values
362: TableColumn col = colmod
363: .getColumn(ProcedureReader.COLUMN_IDX_PROC_COLUMNS_JDBC_DATA_TYPE);
364: if (col != null) {
365: col.setCellRenderer(RendererFactory
366: .getSqlTypeRenderer());
367: }
368: procColumns.adjustOrOptimizeColumns();
369: } catch (Exception ex) {
370: LogMgr.logError(
371: "ProcedureListPanel.valueChanged() thread",
372: "Could not read procedure definition", ex);
373: procColumns.reset();
374: }
375:
376: try {
377: sql = meta.getProcedureSource(catalog, schema, proc,
378: type);
379: source.setText(sql == null ? "" : sql.toString());
380: } catch (Throwable ex) {
381: sql = null;
382: LogMgr.logError(
383: "ProcedureListPanel.valueChanged() thread",
384: "Could not read procedure source", ex);
385: source.setText(ex.getMessage());
386: }
387: } finally {
388: WbSwingUtilities.showDefaultCursor(parent);
389: dbConnection.setBusy(false);
390: }
391: // The package name is stored in the catalog field if this is an Oracle database
392: final int pos = findOracleProcedureInPackage(sql, catalog, proc);
393:
394: EventQueue.invokeLater(new Runnable() {
395: public void run() {
396: source.setCaretPosition(pos, (pos > 0));
397: source.requestFocusInWindow();
398: }
399: });
400: }
401:
402: private int findOracleProcedureInPackage(CharSequence sql,
403: String packageName, String procName) {
404: if (sql == null)
405: return 0;
406: if (this .dbConnection == null)
407: return 0;
408: if (!this .dbConnection.getMetadata().isOracle())
409: return 0;
410:
411: if (StringUtil.isEmptyString(packageName))
412: return 0;
413: int pos = OraclePackageParser.findProcedurePosition(sql,
414: procName);
415:
416: return (pos < 0 ? 0 : pos);
417: }
418:
419: public TableIdentifier getObjectTable() {
420: return null;
421: }
422:
423: public Component getComponent() {
424: return this ;
425: }
426:
427: public WbConnection getConnection() {
428: return this .dbConnection;
429: }
430:
431: public List<? extends DbObject> getSelectedObjects() {
432: if (this .procList.getSelectedRowCount() == 0)
433: return null;
434: int rows[] = this .procList.getSelectedRows();
435: int count = rows.length;
436: List<ProcedureDefinition> result = new ArrayList<ProcedureDefinition>(
437: count);
438: if (count == 0)
439: return result;
440:
441: for (int i = 0; i < count; i++) {
442: String name = this .procList.getValueAsString(rows[i],
443: ProcedureReader.COLUMN_IDX_PROC_LIST_NAME);
444:
445: // MS SQL Server appends a semicolon at the end of the name...
446: if (name.indexOf(';') > 0) {
447: name = name.substring(0, name.indexOf(';'));
448: }
449:
450: String proc = this .procList.getValueAsString(rows[i],
451: ProcedureReader.COLUMN_IDX_PROC_LIST_NAME);
452: String schema = this .procList.getValueAsString(rows[i],
453: ProcedureReader.COLUMN_IDX_PROC_LIST_SCHEMA);
454: String catalog = this .procList.getValueAsString(rows[i],
455: ProcedureReader.COLUMN_IDX_PROC_LIST_CATALOG);
456: int type = this .procList.getDataStore().getValueAsInt(
457: rows[i], ProcedureReader.COLUMN_IDX_PROC_LIST_TYPE,
458: DatabaseMetaData.procedureResultUnknown);
459: ProcedureDefinition def = new ProcedureDefinition(catalog,
460: schema, proc, type, this .dbConnection.getMetadata()
461: .isOracle());
462: result.add(def);
463: }
464: return result;
465: }
466:
467: public void reload() {
468: this.reset();
469: this.retrieve();
470: }
471:
472: }
|