001: /*
002: * Copyright 2006-2007 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt.
007: *
008: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
009: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
010: * the license for the specific language governing your rights and limitations.
011: *
012: * Additional Contributor(s): Martin Schmid gridvision engineering GmbH
013: */
014: package org.pentaho.reportdesigner.crm.report.datasetplugin.jdbc;
015:
016: import org.jetbrains.annotations.NonNls;
017: import org.jetbrains.annotations.NotNull;
018: import org.jetbrains.annotations.Nullable;
019: import org.pentaho.reportdesigner.crm.report.IconLoader;
020: import org.pentaho.reportdesigner.crm.report.connection.ColumnInfo;
021: import org.pentaho.reportdesigner.crm.report.connection.MetaDataService;
022: import org.pentaho.reportdesigner.lib.client.components.docking.IconCreator;
023: import org.pentaho.reportdesigner.lib.client.i18n.TranslationManager;
024: import org.pentaho.reportdesigner.lib.client.util.UncaughtExcpetionsModel;
025:
026: import javax.swing.*;
027: import javax.swing.event.TreeExpansionEvent;
028: import javax.swing.event.TreeWillExpandListener;
029: import javax.swing.tree.DefaultMutableTreeNode;
030: import javax.swing.tree.DefaultTreeCellRenderer;
031: import javax.swing.tree.DefaultTreeModel;
032: import javax.swing.tree.TreePath;
033: import java.awt.*;
034: import java.awt.datatransfer.DataFlavor;
035: import java.awt.datatransfer.Transferable;
036: import java.awt.datatransfer.UnsupportedFlavorException;
037: import java.lang.reflect.InvocationTargetException;
038: import java.util.logging.Level;
039: import java.util.logging.Logger;
040:
041: /**
042: * User: Martin
043: * Date: 12.02.2006
044: * Time: 15:28:49
045: */
046: public class DatabaseSchemaTree extends JTree {
047: @NonNls
048: @NotNull
049: private static final Logger LOG = Logger
050: .getLogger(DatabaseSchemaTree.class.getName());
051:
052: @NotNull
053: private DefaultTreeModel defaultTreeModel;
054:
055: public DatabaseSchemaTree(@NotNull
056: final MetaDataService metaDataService) {
057: RootNode rootNode = new RootNode();
058: DatabaseNode databaseNode = new DatabaseNode(metaDataService
059: .getCatalog());
060: rootNode.add(databaseNode);
061:
062: defaultTreeModel = new DefaultTreeModel(rootNode);
063: defaultTreeModel.setAsksAllowsChildren(true);
064:
065: setShowsRootHandles(true);
066: setRootVisible(false);
067:
068: setModel(defaultTreeModel);
069:
070: addTreeWillExpandListener(new TreeWillExpandListener() {
071: public void treeWillExpand(@NotNull
072: TreeExpansionEvent event) {
073: DefaultMutableTreeNode node = (DefaultMutableTreeNode) event
074: .getPath().getLastPathComponent();
075: if (node.getChildCount() == 0) {
076: fetchNode(node, metaDataService);
077: }
078: }
079:
080: public void treeWillCollapse(@NotNull
081: TreeExpansionEvent event) {
082: }
083: });
084:
085: setCellRenderer(new DefaultTreeCellRenderer() {
086: @NotNull
087: public Component getTreeCellRendererComponent(@NotNull
088: JTree tree, @Nullable
089: Object value, boolean sel, boolean expanded, boolean leaf,
090: int row, boolean hasFocus) {
091: JLabel label = (JLabel) super
092: .getTreeCellRendererComponent(tree, value, sel,
093: expanded, leaf, row, hasFocus);
094: try {
095: if (value instanceof DatabaseNode) {
096: DatabaseNode node = (DatabaseNode) value;
097: String catalog = node.getCatalog();
098: if (catalog == null) {
099: catalog = TranslationManager
100: .getInstance()
101: .getTranslation("R",
102: "SchemaTree.DatabaseNode.Title");
103: }
104: label.setText(catalog);
105: if (node.getState() > 0) {
106: ImageIcon overlayImageIcon = IconCreator
107: .createOverlayImageIcon(
108: IconLoader.getInstance()
109: .getDataSetsIcon(),
110: IconLoader.getInstance()
111: .getSanduhrs()[(node
112: .getState() * 2) % 14]);
113: label.setIcon(overlayImageIcon);
114: } else {
115: label.setIcon(IconLoader.getInstance()
116: .getDataSetsIcon());
117: }
118: } else if (value instanceof SchemaNode) {
119: SchemaNode schemaNode = (SchemaNode) value;
120: label.setText(schemaNode.getSchema());
121: if (schemaNode.getState() > 0) {
122: ImageIcon overlayImageIcon = IconCreator
123: .createOverlayImageIcon(
124: IconLoader
125: .getInstance()
126: .getDatabaseSchemaIcon(),
127: IconLoader.getInstance()
128: .getSanduhrs()[(schemaNode
129: .getState() * 2) % 14]);
130: label.setIcon(overlayImageIcon);
131: } else {
132: label.setIcon(IconLoader.getInstance()
133: .getDatabaseSchemaIcon());
134: }
135: } else if (value instanceof TableNode) {
136: TableNode tableNode = (TableNode) value;
137: label.setText(tableNode.getTable());
138: if (tableNode.getState() > 0) {
139: ImageIcon overlayImageIcon = IconCreator
140: .createOverlayImageIcon(
141: IconLoader
142: .getInstance()
143: .getDatabaseTableIcon(),
144: IconLoader.getInstance()
145: .getSanduhrs()[(tableNode
146: .getState() * 2) % 14]);
147: label.setIcon(overlayImageIcon);
148: } else {
149: label.setIcon(IconLoader.getInstance()
150: .getDatabaseTableIcon());
151: }
152: } else if (value instanceof ColumnNode) {
153: ColumnNode columnNode = (ColumnNode) value;
154: label.setText(columnNode.getColumnInfo()
155: .getColumnName()
156: + " ("
157: + columnNode.getColumnInfo()
158: .getColumnClass()
159: .getSimpleName() + ")");
160: if (columnNode.getState() > 0) {
161: ImageIcon overlayImageIcon = IconCreator
162: .createOverlayImageIcon(
163: IconLoader
164: .getInstance()
165: .getDatabaseColumnIcon(),
166: IconLoader.getInstance()
167: .getSanduhrs()[(columnNode
168: .getState() * 2) % 14]);
169: label.setIcon(overlayImageIcon);
170: } else {
171: label.setIcon(IconLoader.getInstance()
172: .getDatabaseColumnIcon());
173: }
174:
175: }
176: } catch (Throwable e) {
177: UncaughtExcpetionsModel.getInstance().addException(
178: e);
179: }
180:
181: return label;
182: }
183: });
184:
185: initDragAndDrop();
186: }
187:
188: private void fetchNode(@NotNull
189: final DefaultMutableTreeNode node, @NotNull
190: final MetaDataService metaDataService) {
191: Thread t = new Thread() {
192: private transient boolean running;
193:
194: public void run() {
195: running = true;
196: Thread stateUpdateThread = new Thread() {
197: public void run() {
198: try {
199: while (running) {
200: try {
201: Thread.sleep(50);
202: if (!running) {
203: return;
204: }
205: } catch (InterruptedException e) {
206: if (LOG.isLoggable(Level.FINE))
207: LOG
208: .log(
209: Level.FINE,
210: "DatabaseSchemaTree.run ",
211: e);
212: }
213: if (node instanceof ProgressNode) {
214: try {
215: EventQueue
216: .invokeAndWait(new Runnable() {
217: public void run() {
218: ProgressNode progressNode = (ProgressNode) node;
219: progressNode
220: .setState(progressNode
221: .getState() + 1);
222: Rectangle pathBounds = getPathBounds(new TreePath(
223: node
224: .getPath()));
225: if (pathBounds != null) {
226: repaint(pathBounds);
227: }
228: }
229: });
230: } catch (InterruptedException e) {
231: if (LOG.isLoggable(Level.FINE))
232: LOG
233: .log(
234: Level.FINE,
235: "DatabaseSchemaTree.run ",
236: e);
237: } catch (InvocationTargetException e) {
238: if (LOG.isLoggable(Level.FINE))
239: LOG
240: .log(
241: Level.FINE,
242: "DatabaseSchemaTree.run ",
243: e);
244: }
245: }
246: }
247: } finally {
248: try {
249: EventQueue
250: .invokeAndWait(new Runnable() {
251: public void run() {
252: ProgressNode progressNode = (ProgressNode) node;
253: progressNode
254: .setState(0);
255: Rectangle pathBounds = getPathBounds(new TreePath(
256: node.getPath()));
257: if (pathBounds != null) {
258: repaint(pathBounds);
259: }
260: }
261: });
262: } catch (InterruptedException e) {
263: if (LOG.isLoggable(Level.FINE))
264: LOG.log(Level.FINE,
265: "DatabaseSchemaTree.run ",
266: e);
267: } catch (InvocationTargetException e) {
268: if (LOG.isLoggable(Level.FINE))
269: LOG.log(Level.FINE,
270: "DatabaseSchemaTree.run ",
271: e);
272: }
273: }
274: }
275: };
276: stateUpdateThread.setDaemon(true);
277: stateUpdateThread.setPriority(Thread.NORM_PRIORITY - 1);
278: stateUpdateThread.start();
279:
280: if (node instanceof DatabaseNode) {
281: final String[] schemaNames = metaDataService
282: .getSchemaNames();
283:
284: EventQueue.invokeLater(new Runnable() {
285: public void run() {
286: for (String schemaName : schemaNames) {
287: SchemaNode newNode = new SchemaNode(
288: schemaName);
289: defaultTreeModel.insertNodeInto(
290: newNode, node, node
291: .getChildCount());
292: }
293:
294: //workaround for mysql
295: if (schemaNames.length == 0) {
296: SchemaNode newNode = new SchemaNode("");
297: defaultTreeModel.insertNodeInto(
298: newNode, node, node
299: .getChildCount());
300: }
301: }
302: });
303:
304: } else if (node instanceof SchemaNode) {
305: final SchemaNode schemaNode = (SchemaNode) node;
306: final String[] tableNames = metaDataService
307: .getTableNames(schemaNode.getSchema());
308: EventQueue.invokeLater(new Runnable() {
309: public void run() {
310: for (String tableName : tableNames) {
311: TableNode newNode = new TableNode(
312: schemaNode.getSchema(),
313: tableName);
314: defaultTreeModel.insertNodeInto(
315: newNode, node, node
316: .getChildCount());
317: }
318: }
319: });
320:
321: } else if (node instanceof TableNode) {
322: final TableNode tableNode = (TableNode) node;
323: final ColumnInfo[] columnInfos = metaDataService
324: .getColumnInfos(tableNode.getSchema(),
325: tableNode.getTable());
326: EventQueue.invokeLater(new Runnable() {
327: public void run() {
328: for (ColumnInfo columnInfo : columnInfos) {
329: ColumnNode newNode = new ColumnNode(
330: tableNode.getSchema(),
331: tableNode.getTable(),
332: columnInfo);
333: defaultTreeModel.insertNodeInto(
334: newNode, node, node
335: .getChildCount());
336: }
337: }
338: });
339:
340: }
341:
342: running = false;
343: }
344: };
345: t.setDaemon(true);
346: t.setPriority(Thread.NORM_PRIORITY - 1);
347: t.start();
348: }
349:
350: private void initDragAndDrop() {
351: setTransferHandler(new TransferHandler() {
352: @NotNull
353: protected Transferable createTransferable(@NotNull
354: JComponent c) {
355: return new Transferable() {
356: @NotNull
357: public DataFlavor[] getTransferDataFlavors() {
358: return new DataFlavor[] { DataFlavor.stringFlavor };
359: }
360:
361: public boolean isDataFlavorSupported(@NotNull
362: DataFlavor flavor) {
363: //noinspection ObjectEquality
364: return DataFlavor.stringFlavor == flavor;
365: }
366:
367: @Nullable
368: public Object getTransferData(@NotNull
369: DataFlavor flavor)
370: throws UnsupportedFlavorException {
371: //noinspection ObjectEquality
372: if (DataFlavor.stringFlavor == flavor) {
373: TreePath[] selectionPaths = getSelectionPaths();
374: if (selectionPaths != null
375: && selectionPaths.length == 1) {
376: TreePath tp = selectionPaths[0];
377: if (tp.getLastPathComponent() instanceof TableNode) {
378: TableNode tableNode = (TableNode) tp
379: .getLastPathComponent();
380: return tableNode.getTable();
381: } else if (tp.getLastPathComponent() instanceof ColumnNode) {
382: ColumnNode columnNode = (ColumnNode) tp
383: .getLastPathComponent();
384: return columnNode.getTable()
385: + "."
386: + columnNode
387: .getColumnInfo()
388: .getColumnName();
389: }
390: } else if (selectionPaths != null) {
391: StringBuilder sb = new StringBuilder(32);
392: boolean first = true;
393: for (TreePath treePath : selectionPaths) {
394: if (treePath.getLastPathComponent() instanceof ColumnNode) {
395: ColumnNode columnNode = (ColumnNode) treePath
396: .getLastPathComponent();
397: if (!first) {
398: sb.append(", ");
399: }
400: sb
401: .append(
402: columnNode
403: .getTable())
404: .append(".")
405: .append(
406: columnNode
407: .getColumnInfo()
408: .getColumnName());
409: first = false;
410: }
411: }
412: return sb.toString();
413: }
414:
415: return null;
416: } else {
417: throw new UnsupportedFlavorException(flavor);
418: }
419: }
420: };
421: }
422:
423: public int getSourceActions(@NotNull
424: JComponent c) {
425: TreePath[] paths = getSelectionPaths();
426: if (paths != null && paths.length == 1) {
427: if (paths[0].getLastPathComponent() instanceof RootNode) {
428: return TransferHandler.NONE;
429: } else if (paths[0].getLastPathComponent() instanceof DatabaseNode) {
430: return TransferHandler.NONE;
431: } else if (paths[0].getLastPathComponent() instanceof SchemaNode) {
432: return TransferHandler.NONE;
433: } else {
434: return TransferHandler.COPY;
435: }
436: } else if (paths != null) {
437: for (TreePath treePath : paths) {
438: if (!(treePath.getLastPathComponent() instanceof ColumnNode)) {
439: return TransferHandler.NONE;
440: }
441: }
442: return TransferHandler.COPY;
443: }
444: return TransferHandler.NONE;
445: }
446: });
447: setDragEnabled(true);
448: }
449:
450: public static class ProgressNode extends DefaultMutableTreeNode {
451: private int state;
452:
453: public int getState() {
454: return state;
455: }
456:
457: public void setState(int state) {
458: this .state = state;
459: }
460: }
461:
462: public static class RootNode extends ProgressNode {
463: public RootNode() {
464: }
465:
466: public boolean getAllowsChildren() {
467: return true;
468: }
469: }
470:
471: public static class DatabaseNode extends ProgressNode {
472: @NotNull
473: private String catalog;
474:
475: public DatabaseNode(@NotNull
476: String catalog) {
477: this .catalog = catalog;
478: }
479:
480: @NotNull
481: public String getCatalog() {
482: return catalog;
483: }
484:
485: public boolean getAllowsChildren() {
486: return true;
487: }
488: }
489:
490: public static class SchemaNode extends ProgressNode {
491: @NotNull
492: private String schema;
493:
494: public SchemaNode(@NotNull
495: String schema) {
496: this .schema = schema;
497: }
498:
499: @NotNull
500: public String getSchema() {
501: return schema;
502: }
503:
504: public boolean getAllowsChildren() {
505: return true;
506: }
507:
508: @NotNull
509: public String toString() {
510: return "SchemaNode{" + "schema='" + schema + "'" + "}";
511: }
512: }
513:
514: public static class TableNode extends ProgressNode {
515: @NotNull
516: private String schema;
517: @NotNull
518: private String table;
519:
520: public TableNode(@NotNull
521: String schema, @NotNull
522: String table) {
523: this .schema = schema;
524: this .table = table;
525: }
526:
527: @NotNull
528: public String getSchema() {
529: return schema;
530: }
531:
532: @NotNull
533: public String getTable() {
534: return table;
535: }
536:
537: public boolean getAllowsChildren() {
538: return true;
539: }
540:
541: @NotNull
542: public String toString() {
543: return "TableNode{" + "schema='" + schema + "'"
544: + ", table='" + table + "'" + "}";
545: }
546: }
547:
548: public static class ColumnNode extends ProgressNode {
549: @NotNull
550: private String schema;
551: @NotNull
552: private String table;
553: @NotNull
554: private ColumnInfo columnInfo;
555:
556: public ColumnNode(@NotNull
557: String schema, @NotNull
558: String table, @NotNull
559: ColumnInfo columnInfo) {
560: this .schema = schema;
561: this .table = table;
562:
563: this .columnInfo = columnInfo;
564: }
565:
566: @NotNull
567: public String getSchema() {
568: return schema;
569: }
570:
571: @NotNull
572: public String getTable() {
573: return table;
574: }
575:
576: @NotNull
577: public ColumnInfo getColumnInfo() {
578: return columnInfo;
579: }
580:
581: public boolean getAllowsChildren() {
582: return false;
583: }
584:
585: @NotNull
586: public String toString() {
587: return "ColumnNode{" + "schema='" + schema + "'"
588: + ", table='" + table + "'" + ", columnInfo="
589: + columnInfo + "}";
590: }
591: }
592:
593: }
|