0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.db.sql.visualeditor.querybuilder;
0042:
0043: import org.openide.nodes.AbstractNode;
0044:
0045: import java.awt.BorderLayout;
0046: import java.awt.Dimension;
0047: import java.awt.Color;
0048: import java.awt.Font;
0049: import java.awt.FontMetrics;
0050: import java.awt.Point;
0051: import java.awt.event.*;
0052: import java.awt.Component;
0053: import java.awt.Graphics;
0054: import java.awt.Graphics2D;
0055: import java.awt.Toolkit;
0056: import java.awt.BasicStroke;
0057:
0058: import java.awt.dnd.*;
0059: import java.awt.datatransfer.*;
0060:
0061: import java.awt.font.FontRenderContext;
0062: import java.awt.geom.Rectangle2D;
0063:
0064: import java.util.Collection;
0065: import java.util.Collections;
0066: import java.util.Iterator;
0067: import java.util.ArrayList;
0068: import java.util.List;
0069:
0070: import javax.swing.ImageIcon;
0071: import javax.swing.JPopupMenu;
0072: import javax.swing.JComponent;
0073: import javax.swing.JScrollPane;
0074: import javax.swing.JPanel;
0075: import javax.swing.JEditorPane;
0076: import javax.swing.JDesktopPane;
0077: import javax.swing.JMenu;
0078: import javax.swing.JMenuItem;
0079: import javax.swing.JCheckBoxMenuItem;
0080: import javax.swing.JViewport;
0081: import javax.swing.SwingUtilities;
0082: import javax.swing.UIManager;
0083:
0084: import javax.swing.event.TableModelEvent;
0085: import javax.swing.event.TableModelListener;
0086: import javax.swing.event.InternalFrameEvent;
0087: import javax.swing.event.InternalFrameAdapter;
0088: import javax.swing.table.DefaultTableModel;
0089:
0090: import java.sql.*;
0091:
0092: import org.openide.util.NbBundle;
0093: import org.openide.nodes.Node;
0094: import org.openide.NotifyDescriptor;
0095: import org.openide.DialogDisplayer;
0096:
0097: import org.netbeans.modules.db.sql.visualeditor.Log;
0098:
0099: import org.netbeans.modules.db.sql.visualeditor.querymodel.JoinTable;
0100: import org.netbeans.modules.db.sql.visualeditor.querymodel.Column;
0101: import org.netbeans.modules.db.sql.visualeditor.querymodel.Expression;
0102: import org.netbeans.modules.db.sql.visualeditor.querymodel.Value;
0103: import org.netbeans.modules.db.sql.visualeditor.querymodel.Predicate;
0104: import org.netbeans.modules.db.sql.visualeditor.querymodel.And;
0105: import org.netbeans.modules.db.sql.visualeditor.querymodel.Or;
0106: import org.netbeans.modules.db.sql.visualeditor.querymodel.Table;
0107: import org.netbeans.modules.db.sql.visualeditor.querymodel.Where;
0108: import org.netbeans.modules.db.sql.visualeditor.querymodel.SQLQueryFactory;
0109: import org.netbeans.modules.db.sql.visualeditor.querymodel.ExpressionList;
0110:
0111: import org.netbeans.api.visual.widget.Widget;
0112: import org.netbeans.api.visual.widget.ComponentWidget;
0113: import org.netbeans.api.visual.widget.ConnectionWidget;
0114:
0115: import org.netbeans.api.visual.anchor.AnchorShape;
0116: import org.netbeans.api.visual.anchor.AnchorShapeFactory;
0117:
0118: import org.netbeans.api.visual.action.PopupMenuProvider;
0119: import org.netbeans.api.visual.action.ActionFactory;
0120: import org.netbeans.api.visual.action.SelectProvider;
0121:
0122: /**
0123: *
0124: * @author Sanjay Dhamankar, Jim Davidson
0125: */
0126: public class QueryBuilderGraphFrame extends JPanel implements
0127: ActionListener, TableModelListener, ItemListener, KeyListener,
0128: DropTargetListener, PopupMenuProvider {
0129: // Private/package variables
0130:
0131: private static final boolean DEBUG = false;
0132:
0133: private boolean _disableQBGF = false;
0134:
0135: private QueryBuilder _queryBuilder;
0136:
0137: private DropTarget _dropTarget;
0138:
0139: private JDesktopPane _desktopPane = null;
0140: private QBGFJPanel _canvas = null;
0141: private QueryBuilderInputTable _queryBuilderInputTable;
0142: private DefaultTableModel _inputTableModel;
0143: private JEditorPane _sqlTextArea;
0144: private DefaultTableModel _resultTableModel;
0145:
0146: private QBGraphScene _scene;
0147:
0148: private JScrollPane _desktopScrollPane;
0149: private JViewport viewport;
0150:
0151: private FrameSelectionListener _fsl = null;
0152: // private FrameComponentListener _fcl = null;
0153: private ComponentListener _cl = null;
0154: private JPopupMenu _backgroundPopup;
0155: private JPopupMenu _tableTitlePopup;
0156: private AddTableDlg _addTableDlg = null;
0157:
0158: boolean _firstTableInserted = false;
0159: // private Point _location;
0160:
0161: private QBNodeComponent _selectedNode;
0162:
0163: // constants used in the getNextLocation
0164: private static final int initX = 40;
0165: private static final int initY = 40;
0166:
0167: private static final int offsetX = 20;
0168: private int offsetY = 20;
0169:
0170: private static final int MAX_TABLES_IN_A_ROW = 5;
0171:
0172: private java.util.Random randomVal = new java.util.Random();
0173:
0174: private JMenuItem runQueryMenuItem;
0175: private JMenuItem groupByMenuItem;
0176:
0177: private boolean _checkTableColumnValidity = false;
0178:
0179: private boolean _inputTableAddCriteria = false;
0180:
0181: java.net.URL url_primary_key = getClass()
0182: .getResource(
0183: "/org/netbeans/modules/db/sql/visualeditor/resources/primaryKey.gif"); // NOI18N
0184: java.net.URL url_foreign_key = getClass()
0185: .getResource(
0186: "/org/netbeans/modules/db/sql/visualeditor/resources/foreignKey.gif"); // NOI18N
0187:
0188: // Constructor
0189:
0190: // Note that this takes as parameter the models that underlie the other three panes.
0191: // The QBGF could post listeners on each of those panes; the only one currently
0192: // listened to is inputTableModel
0193: public QueryBuilderGraphFrame(QueryBuilder queryBuilder,
0194: QueryBuilderInputTable queryBuilderInputTable,
0195: JEditorPane sqlTextArea, DefaultTableModel resultTableModel) {
0196: super (new BorderLayout());
0197:
0198: Log.getLogger().entering("QueryBuilderGraphFrame",
0199: "constructor"); // NOI18N
0200:
0201: _queryBuilder = queryBuilder;
0202: _queryBuilderInputTable = queryBuilderInputTable;
0203: _inputTableModel = (DefaultTableModel) _queryBuilderInputTable
0204: .getModel();
0205: _sqlTextArea = sqlTextArea;
0206: _resultTableModel = resultTableModel;
0207:
0208: // Listen for events in the input table (column list)
0209: // Unfortunately, this gets triggered with every change to the model.
0210: // We need a way to make a group of changes to the input table, then invoke the
0211: // listener once at the end.
0212: _inputTableModel.addTableModelListener(this );
0213:
0214: // Get a list of tables in the DB
0215:
0216: JMenu menu, subMenu;
0217: JMenuItem menuItem;
0218:
0219: // Create three listeners
0220: _fsl = new FrameSelectionListener();
0221: // _fcl = new FrameComponentListener();
0222: _cl = new CompListener();
0223:
0224: // Create two popup menus
0225: _backgroundPopup = createBackgroundPopup();
0226: _tableTitlePopup = createTableTitlePopup();
0227:
0228: // Add listener to components that can bring up popup menus.
0229: // Create a listener that will bring up background menu
0230: // MouseListener backgroundPopupListener = new BackgroundPopupListener();
0231:
0232: // Add it as listener
0233: // _graph.addMouseListener(backgroundPopupListener);
0234: // _desktopPane.addMouseListener(backgroundPopupListener);
0235: // _desktopScrollPane.addMouseListener(backgroundPopupListener);
0236:
0237: // Create a new listener for noticing graph selection
0238: // _graph.addGraphSelectionListener(new GraphSelListener());
0239:
0240: // Add the JScrollPane to the QueryBuilderGraphFrame
0241: // this.add(_desktopScrollPane,BorderLayout.CENTER);
0242:
0243: // Make the GraphFrame visible
0244: setVisible(true);
0245:
0246: // _dropTarget = new DropTarget(_queryBuilder.getQueryBuilderPane()._qbSceneView,
0247: // DnDConstants.ACTION_COPY_OR_MOVE, this);
0248: }
0249:
0250: // Set up the links between the QBGraphFrame (old style) and Scene (new style)
0251: void initScene(QBGraphScene scene, JComponent sceneView) {
0252: _scene = scene;
0253: _dropTarget = new DropTarget(sceneView,
0254: DnDConstants.ACTION_COPY_OR_MOVE, this );
0255: }
0256:
0257: void setDropTarget(Component comp) {
0258: _dropTarget = new DropTarget(comp,
0259: DnDConstants.ACTION_COPY_OR_MOVE, this );
0260: }
0261:
0262: // Setter for scene
0263: void setScene(QBGraphScene qbgScene) {
0264: _scene = qbgScene;
0265: }
0266:
0267: void getCanvasFocus() {
0268: _canvas.requestFocus(true);
0269: }
0270:
0271: void setQBGFEnabled(boolean value) {
0272: _disableQBGF = !(value);
0273: if (_disableQBGF) {
0274: int frameWidth = (int) this .getSize().getWidth();
0275: int frameHeight = (int) this .getSize().getHeight();
0276:
0277: // _canvas.setSize(new Dimension( frameWidth, frameHeight ) );
0278: // _canvas.updateUI();
0279: // _desktopPane.revalidate();
0280: }
0281: }
0282:
0283: void resizeDesktop() {
0284: }
0285:
0286: void setActivatedNode(QueryBuilderInternalFrame currentSelectedFrame) {
0287: if (currentSelectedFrame != null) {
0288: TableNode tn = currentSelectedFrame.getNode();
0289: _queryBuilder.setActivatedNodes(new Node[] { tn });
0290: } else {
0291: _queryBuilder.setActivatedNodes(new Node[0]);
0292: }
0293: _queryBuilder.activateActions();
0294: }
0295:
0296: class QBGFJPanel extends JPanel {
0297: protected void paintComponent(Graphics g) {
0298: if (DEBUG) {
0299: System.out
0300: .println(" paintComponent() called _parseErrorMessage = "
0301: + _queryBuilder.getParseErrorMessage()
0302: + "\n"); // NOI18N
0303: }
0304: super .paintComponent(g);
0305: if (_queryBuilder.getParseErrorMessage() != null) {
0306: Graphics2D g2d = (Graphics2D) g;
0307: paintCenteredText(g2d);
0308: if (DEBUG) {
0309: System.out.println(" paintCenteredText() called "
0310: + "\n"); // NOI18N
0311: }
0312: }
0313: }
0314:
0315: /** Paint the "help" text when the page is empty */
0316: private void paintCenteredText(Graphics2D g) {
0317: final int PADDING = 5; // Padding around text that's cleared
0318: final int LINESPACING = 3; // Extra pixels between text lines
0319:
0320: // This implementation is slow/inefficient, but since it's only run
0321: // when the page is empty we know we're not busy
0322: String text;
0323: text = _queryBuilder.getParseErrorMessage();
0324: int textLines = 1;
0325: if (DEBUG) {
0326: System.out
0327: .println(" paintCenteredText() called _parseErrorMessage = "
0328: + _queryBuilder.getParseErrorMessage()
0329: + "\n"); // NOI18N
0330: }
0331: for (int i = 0, n = text.length(); i < n; i++) {
0332: if (text.charAt(i) == '\n') {
0333: textLines++;
0334: }
0335: }
0336:
0337: int width = _desktopScrollPane.getViewport().getWidth();
0338: int height = _desktopScrollPane.getViewport().getHeight();
0339:
0340: this .setSize(new Dimension(width, height));
0341: int center = height / 2;
0342:
0343: Font font = UIManager.getFont("Label.font"); // NOI18N
0344: g.setFont(font);
0345: FontMetrics metrics = Toolkit.getDefaultToolkit()
0346: .getFontMetrics(font);
0347: FontRenderContext frc = g.getFontRenderContext();
0348: int lineHeight = metrics.getHeight() + LINESPACING;
0349:
0350: int top = center - lineHeight * textLines / 2;
0351:
0352: int minx = width;
0353: int maxx = 0;
0354: int nextLine = 0;
0355: for (int line = 0; line < textLines; line++) {
0356: int lineEnd = text.indexOf('\n', nextLine);
0357: String lineText;
0358: if (lineEnd != -1) {
0359: lineText = text.substring(nextLine, lineEnd);
0360: nextLine = lineEnd + 1;
0361: } else {
0362: lineText = text.substring(nextLine);
0363: }
0364:
0365: Rectangle2D bounds1 = font.getStringBounds(lineText,
0366: frc);
0367: int lx = (width - ((int) bounds1.getWidth())) / 2;
0368: if (lx < minx) {
0369: minx = lx;
0370: }
0371: int xw = lx + (int) bounds1.getWidth();
0372: if (xw > maxx) {
0373: maxx = xw;
0374: }
0375: }
0376:
0377: // Clear background under text
0378: Color background = null;
0379: background = getBackground();
0380: g.setColor(background);
0381: int miny = top;
0382: int maxy = top + textLines * lineHeight;
0383: g.fillRect(minx - PADDING, miny, maxx - minx + 2 * PADDING,
0384: maxy - miny + 2 * PADDING);
0385: // Draw text
0386: g.setColor(java.awt.Color.gray);
0387: nextLine = 0;
0388: int y = top + 2 * PADDING; // XXX change to padding constant
0389: y += metrics.getHeight() - metrics.getDescent();
0390: for (int line = 0; line < textLines; line++) {
0391: int lineEnd = text.indexOf('\n', nextLine);
0392: String lineText;
0393: if (lineEnd != -1) {
0394: lineText = text.substring(nextLine, lineEnd);
0395: nextLine = lineEnd + 1;
0396: } else {
0397: lineText = text.substring(nextLine);
0398: }
0399:
0400: Rectangle2D bounds1 = font.getStringBounds(lineText,
0401: frc);
0402: int lx = (width - ((int) bounds1.getWidth())) / 2;
0403:
0404: g.drawString(lineText, lx, y);
0405: y += lineHeight;
0406: }
0407: }
0408: }
0409:
0410: /** Handle the key typed event from the sql text area. */
0411: public void keyTyped(KeyEvent e) {
0412: _checkTableColumnValidity = true;
0413: }
0414:
0415: /** Handle the key pressed event. */
0416: public void keyPressed(KeyEvent e) {
0417: if (DEBUG)
0418: System.out.println(" QBGF : key pressed called. " + "\n"); // NOI18N
0419: _checkTableColumnValidity = true;
0420: if (e.isShiftDown()) {
0421: int code = e.getKeyCode();
0422: switch (code) {
0423: // diagram pane
0424: case KeyEvent.VK_F10:
0425: _backgroundPopup
0426: .show(e.getComponent(),
0427: e.getComponent().getX(), e
0428: .getComponent().getY());
0429: break;
0430: }
0431: }
0432: _queryBuilder.handleKeyPress(e);
0433: }
0434:
0435: /** Handle the key released event from the sql text area. */
0436: public void keyReleased(KeyEvent e) {
0437: _checkTableColumnValidity = true;
0438: }
0439:
0440: public void setTableColumnValidity(boolean value) {
0441: _checkTableColumnValidity = value;
0442: }
0443:
0444: // check the table and column validity only if the user manually
0445: // changes the sql query.
0446: public boolean checkTableColumnValidity() {
0447: return (_checkTableColumnValidity && _queryBuilder
0448: .getQueryBuilderPane().getQueryBuilderSqlTextArea()
0449: .queryChanged());
0450: }
0451:
0452: // Adding a method to access _graph as per QE request
0453:
0454: // Create a popup menu that will appear when the user clicks on the background
0455: // and similar places.
0456: JPopupMenu createBackgroundPopup() {
0457:
0458: JPopupMenu backgroundPopup;
0459: JMenu menu, subMenu;
0460: JMenuItem menuItem;
0461: JMenuItem subMenuItem;
0462:
0463: //Create the popup menu.
0464: backgroundPopup = new JPopupMenu();
0465:
0466: runQueryMenuItem = new JMenuItem(NbBundle.getMessage(
0467: QueryBuilderGraphFrame.class, "RUN_QUERY")); // NOI18N
0468: runQueryMenuItem.addActionListener(this );
0469: backgroundPopup.add(runQueryMenuItem);
0470:
0471: menuItem = new JMenuItem(NbBundle.getMessage(
0472: QueryBuilderGraphFrame.class, "Add_Table")); // NOI18N
0473: menuItem.addActionListener(this );
0474: backgroundPopup.add(menuItem);
0475:
0476: groupByMenuItem = new JCheckBoxMenuItem(NbBundle.getMessage(
0477: QueryBuilderGraphFrame.class, "GROUP_BY")); // NOI18N
0478: groupByMenuItem.addItemListener(this );
0479: backgroundPopup.add(groupByMenuItem);
0480:
0481: return (backgroundPopup);
0482: }
0483:
0484: // Create a menu that will appear when the user clicks on the title bar of a node (table)
0485:
0486: JPopupMenu createTableTitlePopup() {
0487:
0488: JPopupMenu tableTitlePopup;
0489: JMenu menu, subMenu;
0490: JMenuItem menuItem;
0491: JMenuItem subMenuItem;
0492:
0493: // Create the popup menu.
0494: tableTitlePopup = new JPopupMenu();
0495:
0496: menuItem = new JMenuItem(NbBundle.getMessage(
0497: QueryBuilderGraphFrame.class, "REMOVE_FROM_QUERY")); // NOI18N
0498: menuItem.addActionListener(this );
0499: tableTitlePopup.add(menuItem);
0500:
0501: return (tableTitlePopup);
0502: }
0503:
0504: // A class for listeners that will bring up the TableTitlePopup menu.
0505: // An instance of this class will listen on every new InternalFrame
0506: // (table node)
0507:
0508: class TableTitlePopupListener extends MouseAdapter {
0509:
0510: public void mousePressed(MouseEvent e) {
0511: maybeShowPopup(e);
0512: }
0513:
0514: public void mouseReleased(MouseEvent e) {
0515: maybeShowPopup(e);
0516: }
0517:
0518: private void maybeShowPopup(MouseEvent e) {
0519: if (e.isPopupTrigger() && e.getComponent().isEnabled()) {
0520: _tableTitlePopup.show(e.getComponent(), e.getX(), e
0521: .getY());
0522: }
0523: }
0524: }
0525:
0526: // Specified by PopupMenuProvider
0527: public JPopupMenu getPopupMenu(Widget widget, Point localLocation) {
0528: if (widget instanceof QBGraphScene)
0529: return _backgroundPopup;
0530: else {
0531: // We have a Widget wrapping a QBNodeComponent
0532: QBNodeComponent qbNC = (QBNodeComponent) _scene
0533: .findObject(widget);
0534: this ._selectedNode = qbNC;
0535: return _tableTitlePopup;
0536: }
0537: }
0538:
0539: // Returns just the class name -- no package info.
0540: protected String getClassName(Object o) {
0541: String classString = o.getClass().getName();
0542: int dotIndex = classString.lastIndexOf("."); // NOI18N
0543: return classString.substring(dotIndex + 1);
0544: }
0545:
0546: // Specified by TableModelListener.
0547: // Invoked when a TableModel generates an event; implemented by JTable.
0548: // Events come from both QueryBuilderTableModel and
0549: // QueryBuilderInputTableModel,
0550: // The QueryBuilderTableModel indicates columns that are
0551: // selected/deselected; that in turn
0552: // causes them to be added/dropped from the InputTable
0553:
0554: public void tableChanged(TableModelEvent e) {
0555:
0556: // if the graph is disabled, do not handle any events.
0557: if (_disableQBGF)
0558: return;
0559:
0560: // if the graph is being generated from model, do not handle events.
0561: if (_queryBuilder._updateModel == false)
0562: return;
0563:
0564: Log.getLogger().finest(
0565: "Entering QBGF.tableChanged, source: " + e.getSource()); // NOI18N
0566:
0567: if (e.getSource() instanceof QueryBuilderTableModel)
0568: tableModelChanged(e);
0569:
0570: else if (e.getSource() instanceof QueryBuilderInputTableModel)
0571: inputTableModelChanged(e);
0572: }
0573:
0574: // Called when we have received a change in a graph node
0575:
0576: private void tableModelChanged(TableModelEvent e) {
0577:
0578: Log.getLogger().entering("QueryBuilderGraphFrame",
0579: "tableModelChanged");
0580:
0581: // We have a mouse click inside a graph table node, indicating select/deselect.
0582: // Propagate the information to the input table
0583:
0584: // Extract some information from the event
0585: int row = e.getFirstRow(); // the first row that changed
0586: int column = e.getColumn(); // the column for this event
0587:
0588: QueryBuilderTableModel model = (QueryBuilderTableModel) e
0589: .getSource();
0590: String tableSpec = model.getTableSpec();
0591:
0592: // DB column name
0593: String columnName = (String) model.getValueAt(row, column + 2);
0594:
0595: // boolean - Selected/deselected
0596: Object value = model.getValueAt(row, column);
0597:
0598: if (value == Boolean.TRUE) { // A column has been selected
0599:
0600: // Update the query model if appropriate
0601: // Do this first so that it's available when adding the row
0602: if (_queryBuilder._updateModel) {
0603: _queryBuilder.getQueryModel().addColumn(tableSpec,
0604: columnName);
0605: _queryBuilderInputTable.selectColumn(tableSpec,
0606: columnName, Boolean.TRUE);
0607: }
0608: }
0609:
0610: else if (value == Boolean.FALSE) { // A column has been deselected
0611:
0612: // Update the query model, if we're not being driven by it
0613: // Do this before updating the grid, because we use the model to generate sortorder
0614: if (_queryBuilder._updateModel) {
0615: _queryBuilder.getQueryModel().removeColumn(tableSpec,
0616: columnName);
0617: }
0618:
0619: // do not remove the whole row, just deselect the output column.
0620: _queryBuilderInputTable.selectColumn(tableSpec, columnName,
0621: Boolean.FALSE);
0622: }
0623:
0624: // We used to update the text query after every event. That
0625: // caused degraded performance. Now, we check whether we've
0626: // received a real event, or we're generating the graph as a
0627: // batch operation. Also, we trigger only on TableModel events,
0628: // so InputTableMode must explicitly invoke
0629: if (_queryBuilder._updateText) {
0630: // An interactive event -- update the text query
0631: _queryBuilder.generateText();
0632: }
0633: }
0634:
0635: /**
0636: * Called when we have received a change in the input table (grid pane)
0637: * either interactively or because the model was updated from somewhere else
0638: * @param e the event
0639: */
0640: private void inputTableModelChanged(TableModelEvent e) {
0641:
0642: Log.getLogger().entering("QueryBuilderGraphFrame",
0643: "inputTableModelChanged");
0644:
0645: // if _inputTableAddCriteria is true we should not handle any
0646: // events. This is set when the events are not directly generated
0647: // by the user interaction. e.g. when we set value of say
0648: // Criteria column, CriteriaOrder column will be set a value.
0649: // To avoid the recursive calls this is used.
0650: if (_inputTableAddCriteria)
0651: return;
0652:
0653: // Only pay attention to changes to the output column
0654: // Propagate information to the graph node for this table
0655:
0656: // ** We could also do this by listening to the checkbox for
0657: // the particular column in QueryBuilderInputTable, the same
0658: // as we do for SortType, SortOrder, and AddCriteria **
0659:
0660: // Extract some information from the event, and dispatch on column
0661: int column = e.getColumn(); // the column for this event
0662: int row = e.getFirstRow(); // the first row that changed
0663: QueryBuilderInputTableModel model = (QueryBuilderInputTableModel) e
0664: .getSource();
0665:
0666: // Column -1 indicates ...
0667: if (column != -1) {
0668:
0669: String columnName = (String) model.getValueAt(row,
0670: QueryBuilderInputTable.Column_COLUMN);
0671: String tableSpec = (String) model.getValueAt(row,
0672: QueryBuilderInputTable.Table_COLUMN);
0673:
0674: if (DEBUG)
0675: System.out.println("QBGF.iTMC, row: " + row
0676: + " columnName: " + columnName + // NOI18N
0677: " column: " + column); // NOI18N
0678:
0679: if (column == QueryBuilderInputTable.Alias_COLUMN) {
0680:
0681: String result = ((String) model.getValueAt(row,
0682: QueryBuilderInputTable.Alias_COLUMN)).trim();
0683: if (result == null || result.length() == 0) // Clear alias
0684: _queryBuilder.getQueryModel().removeDerivedColName(
0685: tableSpec, columnName);
0686: else
0687: _queryBuilder.getQueryModel().setDerivedColName(
0688: tableSpec, columnName, result);
0689: }
0690: if (column == QueryBuilderInputTable.Output_COLUMN) {
0691:
0692: Boolean value = (Boolean) model.getValueAt(row, column); // Selected/deselected
0693: QBNodeComponent node = findGraphNode(tableSpec);
0694: QueryBuilderTableModel qbtm = node
0695: .getQueryBuilderTableModel();
0696: qbtm.selectColumn(columnName, value);
0697: // Don't regenerate query, since selectColumn will cause that to happen
0698: return;
0699: } else if (column == QueryBuilderInputTable.Criteria_COLUMN) {
0700:
0701: String result = ((String) model.getValueAt(row,
0702: QueryBuilderInputTable.Criteria_COLUMN)).trim();
0703:
0704: // The the value is "" remove the criteria order from the combo box
0705: if (result != null && result.length() == 0) {
0706: // The following lines appear to be unnecessary
0707: _inputTableAddCriteria = true;
0708: model
0709: .setValueAt(
0710: "",
0711: row,
0712: QueryBuilderInputTable.CriteriaOrder_COLUMN); // NOI18N
0713: _inputTableAddCriteria = false;
0714: _queryBuilder.getQueryModel().removeCriteria(
0715: tableSpec, columnName, 1);
0716: } else if (result
0717: .trim()
0718: .equals(
0719: QueryBuilderInputTable.Criteria_Uneditable_String)) {
0720: return;
0721: } else {
0722: Predicate pred = checkCriteria(tableSpec,
0723: columnName, result);
0724: if (pred == null) {
0725: _queryBuilderInputTable.getModel().setValueAt(
0726: "", row, column); // NOI18N
0727: return;
0728: }
0729:
0730: int criteriaCount = _queryBuilder.getQueryModel()
0731: .getCriteriaCount();
0732: String order = (String) model
0733: .getValueAt(
0734: row,
0735: QueryBuilderInputTable.CriteriaOrder_COLUMN);
0736: if (order != null
0737: && order.trim().length() != 0
0738: && order
0739: .trim()
0740: .equals(
0741: QueryBuilderInputTable.CriteriaOrder_Uneditable_String)) {
0742: return;
0743: }
0744: int orderNum = ((order == null) || (order.trim()
0745: .length() == 0)) ? criteriaCount + 1
0746: : Integer.parseInt(order);
0747: _inputTableAddCriteria = true;
0748: model
0749: .setValueAt(
0750: new Integer(orderNum).toString(),
0751: row,
0752: QueryBuilderInputTable.CriteriaOrder_COLUMN);
0753: _inputTableAddCriteria = false;
0754: if (orderNum < criteriaCount + 1) {
0755: _queryBuilder.getQueryModel().replaceCriteria(
0756: tableSpec, columnName, pred, orderNum);
0757: } else {
0758: _queryBuilder.getQueryModel().addCriteria(
0759: tableSpec, columnName, pred);
0760: }
0761: }
0762: _queryBuilderInputTable.clearSelection();
0763: _inputTableAddCriteria = true;
0764: // Regenerate the input table, based on the latest change.
0765: // Is this necessary? May be required to get Criteria Order right
0766: _queryBuilderInputTable
0767: .generateTableWhere(_queryBuilder
0768: .getQueryModel());
0769: _inputTableAddCriteria = false;
0770: } else if (column == QueryBuilderInputTable.CriteriaOrder_COLUMN) {
0771:
0772: String criteria = ((String) model.getValueAt(row,
0773: QueryBuilderInputTable.Criteria_COLUMN)).trim();
0774: String order = (String) model.getValueAt(row, column);
0775:
0776: if ((order != null && order.trim().length() != 0)
0777: && (order.trim()
0778: .equals(QueryBuilderInputTable.CriteriaOrder_Uneditable_String))) {
0779: return;
0780: }
0781: // remove the old criteria first anyway
0782: _queryBuilder.getQueryModel().removeCriteria(tableSpec,
0783: columnName, 1);
0784:
0785: if ((order == null) || (order.trim().length() == 0)) {
0786: _inputTableAddCriteria = true;
0787: model.setValueAt("", row,
0788: QueryBuilderInputTable.Criteria_COLUMN); // NOI18N
0789: _inputTableAddCriteria = false;
0790: } else {
0791: int orderNum = Integer.parseInt(order);
0792: Predicate pred = checkCriteria(tableSpec,
0793: columnName, criteria);
0794: if (pred == null) {
0795: _queryBuilderInputTable.getModel().setValueAt(
0796: "", row, column); // NOI18N
0797: return;
0798: }
0799: if (criteria.trim().length() != 0) {
0800: _queryBuilder.getQueryModel().addCriteria(
0801: tableSpec, columnName, pred, orderNum);
0802: }
0803: }
0804: _queryBuilderInputTable.clearSelection();
0805: _inputTableAddCriteria = true;
0806: _queryBuilderInputTable
0807: .generateTableWhere(_queryBuilder
0808: .getQueryModel());
0809: _inputTableAddCriteria = false;
0810: }
0811: // Regenerate the query, if someone else isn't doing it
0812: if (_queryBuilder._updateText) {
0813: _queryBuilder.generateText();
0814: }
0815: }
0816: }
0817:
0818: /**
0819: * Given a criteria string retrun Predicate or null if result is
0820: * an error. Displays appropriate message.
0821: */
0822: private Predicate checkCriteria(String tableSpec,
0823: String columnName, String result) {
0824: String op = null;
0825: String val = null;
0826: if (result.startsWith(">=")) { // NOI18N
0827: op = ">="; // NOI18N
0828: val = result.substring(2).trim();
0829: } else if (result.startsWith("<=")) { // NOI18N
0830: op = "<="; // NOI18N
0831: val = result.substring(2).trim();
0832: } else if (result.startsWith("<>")) { // NOI18N
0833: op = "<>"; // NOI18N
0834: val = result.substring(2).trim();
0835: } else if (result.startsWith(">")) { // NOI18N
0836: op = ">"; // NOI18N
0837: val = result.substring(1).trim();
0838: } else if (result.startsWith("<")) { // NOI18N
0839: op = "<"; // NOI18N
0840: val = result.substring(1).trim();
0841: } else if (result.startsWith("=")) { // NOI18N
0842: op = "="; // NOI18N
0843: val = result.substring(1).trim();
0844: } else if (result.toUpperCase().startsWith("LIKE")) { // NOI18N
0845: op = " LIKE "; // NOI18N
0846: val = result.substring(5).trim();
0847: } else if (result.toUpperCase().startsWith("IN")) { // NOI18N
0848: op = " IN "; // NOI18N
0849: val = result.substring(3).trim();
0850: }
0851:
0852: // if val or op is still null
0853: // display an error and clear the cell and return.
0854: if (op == null || (op.trim().length() == 0) || val == null
0855: || (val.trim().length() == 0)) {
0856: String msg = NbBundle.getMessage(
0857: QueryBuilderGraphFrame.class, "CRITERIA_ERROR"); // NOI18N
0858: NotifyDescriptor d = new NotifyDescriptor.Message(msg
0859: + "\n\n", NotifyDescriptor.ERROR_MESSAGE); // NOI18N
0860: DialogDisplayer.getDefault().notify(d);
0861: _queryBuilderInputTable.clearSelection();
0862: return null;
0863: }
0864:
0865: Column col1 = SQLQueryFactory.createColumn(tableSpec,
0866: columnName);
0867: Predicate pred = SQLQueryFactory.createPredicate(col1, val, op);
0868:
0869: return pred;
0870: }
0871:
0872: public void setCurrentSelectedFrameTitle(String title) {
0873: if ((QueryBuilderInternalFrame) _desktopPane.getSelectedFrame() != null)
0874: ((QueryBuilderInternalFrame) _desktopPane
0875: .getSelectedFrame()).setTitle(title);
0876: }
0877:
0878: // Add a node (representing a database table) to the query graph
0879: // If _updateModel is true, add edges corresponding to any FK relationships
0880: // ToDo: Add provisions for selecting only some of the columns in the table
0881: // for inclusion in the query.
0882: private void insertTableInteractively(String fullTableName) {
0883: Log.getLogger().entering("QueryBuilderGraphFrame",
0884: "insertTableInteractively", fullTableName); // NOI18N
0885:
0886: // fix for 6316681 Opening QE on a rowset where command=null throws NPE
0887: // If the query model is null then the QueryBuilder was not opened
0888: // before. So for the initial table populate the command
0889: // This will initiate the QueryBuilder properly.
0890: if ((_queryBuilder.getQueryModel() == null)
0891: || (_queryBuilder.getQueryModel().genText() == null)) {
0892: String query = "select * from " + fullTableName;
0893: _queryBuilder.populate(query, false);
0894: _queryBuilder.setSqlCommand(query);
0895: return;
0896: }
0897:
0898: // Disable text query re-generation until we're ready
0899: QueryBuilder.showBusyCursor(true);
0900: _queryBuilder._updateText = false;
0901: try {
0902: // Extend to handle full tablespec, including schema
0903: String schemaName = null, tableName, tabName;
0904: String[] res = fullTableName.split("\\."); // NOI18N
0905: if (res.length > 1) {
0906: schemaName = res[0];
0907: tabName = res[1];
0908: } else
0909: tabName = fullTableName;
0910:
0911: /*
0912: if ( !tabName.startsWith("\"") && (tabName.indexOf(' ') != -1) ) {
0913: tableName = new String ("\"" + tabName + "\"" );
0914: }
0915: else {
0916: */
0917: tableName = new String(tabName);
0918: /*
0919: }
0920: */
0921:
0922: // Create the querymodel object representing the table to be added
0923: String corrName = _queryBuilder.getQueryModel()
0924: .genUniqueName(fullTableName);
0925: Table tbl = (corrName == null) ? SQLQueryFactory
0926: .createTable(tableName, null, schemaName)
0927: : SQLQueryFactory.createTable(tableName, corrName,
0928: schemaName);
0929: JoinTable joinTable = SQLQueryFactory.createJoinTable(tbl);
0930:
0931: List columnNames = new ArrayList();
0932: columnNames.add("*"); // NOI18N
0933:
0934: // Insert the table into the model first, so that column insertions can refer to it
0935: _queryBuilder.getQueryModel().insertTable(joinTable);
0936:
0937: // Insert the node into the graph
0938: QBNodeComponent qbNC = insertTable(joinTable, columnNames);
0939: // if ( internalFrame == null ) {
0940: // QueryBuilder.showBusyCursor( false );
0941: // return;
0942: // }
0943:
0944: // Add appropriate edges in the graph to connect the new node to previous ones,
0945: // based entirely on relationships (Foreign Key constraints)
0946: // This should be refactored, to do computation of edges separately
0947: List edges = insertFKEdges(qbNC, fullTableName);
0948:
0949: // Add the relationships for this table into the model, based in the edges
0950: _queryBuilder.getQueryModel().addRelationships(joinTable,
0951: edges);
0952:
0953: // Redraw the new frame. Must be done after adding edges, to work around
0954: // some painting glitches.
0955: // redrawFrame(currentSelectedFrame);
0956: // redrawFrame(internalFrame);
0957:
0958: // try {
0959: // // Make the new frame the selected one
0960: // // internalFrame.setSelected(true);
0961: // // Update the [0,0] cell for the table model; wasn't this already false?
0962: // // This will cause an event to fire, but it's not clear what the intent was.
0963: // // internalFrame.getQueryBuilderTableModel().setValueAt(Boolean.FALSE, 0, 0);
0964: // // _desktopPane.setSelectedFrame(internalFrame);
0965: // // setActivatedNode( internalFrame ) ;
0966: // } catch(PropertyVetoException pve) {
0967: // }
0968:
0969: _firstTableInserted = true;
0970: } finally {
0971: _queryBuilder._updateText = true;
0972: QueryBuilder.showBusyCursor(false);
0973: }
0974: }
0975:
0976: // Add a node to the query graph, based on the parsed query
0977: // ToDo: Generalize this to support joinTable with conjoined predicates
0978:
0979: void insertTableFromModel(JoinTable joinTable, List columnNames) {
0980:
0981: Log.getLogger().entering("QueryBuilderGraphFrame",
0982: "insertTableFromModel", joinTable.getTableSpec()); // NOI18N
0983:
0984: // Save the state of _updateText
0985: boolean updateText = _queryBuilder._updateText;
0986: _queryBuilder._updateText = false;
0987: try {
0988: QBNodeComponent qbNC = insertTable(joinTable, columnNames);
0989: if (qbNC == null)
0990: return;
0991:
0992: // Insert the edges that are explicitly specified in the query
0993: insertJoinEdges(joinTable);
0994:
0995: // redrawFrame(internalFrame);
0996:
0997: // ToDo: This code used to be called twice. Decide whether there was a reason for it.
0998: // try {
0999: // // Make the new frame the selected one
1000: // // internalFrame.setSelected(true);
1001: // // Update the [0,0] cell for the table model; wasn't this already false?
1002: // // This will cause an event to fire, but it's not clear what the intent was.
1003: // // internalFrame.getQueryBuilderTableModel().setValueAt(Boolean.FALSE, 0, 0);
1004: // // _desktopPane.setSelectedFrame(internalFrame);
1005: // _queryBuilder.enableDelete();
1006: // runQueryMenuItem.setEnabled(true);
1007: // groupByMenuItem.setEnabled(true);
1008: // } catch(PropertyVetoException pve) {
1009: // }
1010:
1011: _firstTableInserted = true;
1012: } finally {
1013: _queryBuilder._updateText = updateText;
1014: }
1015: }
1016:
1017: // Insert a table into the query graph
1018:
1019: QBNodeComponent insertTable(JoinTable joinTable, List columnNames) {
1020:
1021: Log.getLogger().entering("QueryBuilderGraphFrame",
1022: "insertTable", new Object[] { joinTable, columnNames }); // NOI18N
1023:
1024: // you can not insert a non-existent table.
1025: String joinTableName = joinTable.getFullTableName();
1026:
1027: // Create the internal frame
1028: QBNodeComponent qbNC = createNode(joinTable, columnNames);
1029:
1030: // // Add a listener to the title bar (or outside edge) of the new frame
1031: // MouseListener tableTitlePopupListener = new TableTitlePopupListener();
1032: // // trying to handle different LNFs
1033: // try {
1034: // if (!System.getProperty("os.name").startsWith("Mac OS")) // NOI18N
1035: // ((BasicInternalFrameUI)internalFrame.getUI()).getNorthPane().addMouseListener(tableTitlePopupListener);
1036: // else
1037: // internalFrame.addMouseListener(tableTitlePopupListener);
1038: // } catch (Exception e) {
1039: // e.printStackTrace();
1040: // }
1041:
1042: // // Hack - add a listener on the QBNodeComponent, which will replace the internal frame
1043: // // _tmpLastInsertedGraphNode.addMouseListener(tableTitlePopupListener);
1044:
1045: // // Insert the cell, without any edges
1046: // Object insertCells[] = new Object[] {insertCell};
1047: // _graphModel.insert(insertCells,null,null,null,null);
1048:
1049: // // Add listeners that notice when the frame is activated, or moved/resized
1050: // internalFrame.addInternalFrameListener(_fsl);
1051: // internalFrame.addComponentListener(_fcl);
1052:
1053: // // Add the internal frame to the desktopPane. Per the JInternalFrame demo this seems to be
1054: // // necessary (marked VERY IMPORTANT), but needs to be undone if we delete it.
1055: // _desktopPane.add(internalFrame);
1056:
1057: refresh();
1058:
1059: return qbNC;
1060: }
1061:
1062: // Create an internal frame, which will represent a table in the graph
1063: // ToDo: Modify this to return a QBNodeComponent
1064: QBNodeComponent createNode(JoinTable joinTable,
1065: List selectColumnNames) {
1066:
1067: String fullTableName = joinTable.getFullTableName();
1068: String[] table = fullTableName.split("\\."); // NOI18N
1069: String tableName = (table.length > 1) ? table[1] : table[0];
1070: String corrName = joinTable.getCorrName();
1071:
1072: String tableSpec = ((corrName != null) ? corrName
1073: : fullTableName);
1074:
1075: Log.getLogger().finest(
1076: "Entering QBGF.createNode, fullTableName: "
1077: + fullTableName + " corrName: " + corrName); // NOI18N
1078:
1079: // Correct case of table name...
1080: try { // TODO JFB shouldn't catch this...
1081: String newS = _queryBuilder
1082: .checkFullTableName(fullTableName);
1083: if (newS != null & !fullTableName.equals(newS)) {
1084: fullTableName = newS;
1085: Log.getLogger()
1086: .finest(
1087: " fullTableName corrected to "
1088: + fullTableName);
1089: }
1090: } catch (SQLException se) {
1091: Log.getLogger()
1092: .finest(" fullTableName " + se.getMessage());
1093: }
1094:
1095: final String[] columnNames = { "", // "Output", // NOI18N
1096: "", // "KeyIcon" // NOI18N
1097: "" // "Column" // NOI18N
1098: };
1099:
1100: //Create initial internal frame
1101: Object[][] dbData;
1102:
1103: List dbColumnNames;
1104: try {
1105: dbColumnNames = _queryBuilder.getColumnNames(fullTableName);
1106: } catch (SQLException sqle) {
1107: dbColumnNames = new ArrayList();
1108: }
1109:
1110: // This data will populate the QueryBuilderTableModel, which represents
1111: // the schema information shown inside the table node
1112: dbData = new Object[dbColumnNames.size()/* +1 */][3];
1113: Iterator iterator = dbColumnNames.iterator();
1114:
1115: // Primary keys and foreign keys are marked with special icons
1116: List primaryKeys = null, foreignKeyCols = null;
1117: try {
1118: primaryKeys = _queryBuilder.getPrimaryKeys(fullTableName);
1119: foreignKeyCols = _queryBuilder
1120: .getImportedKeyColumns(fullTableName);
1121: } catch (SQLException sqle) {
1122: // HACK! log and dispose
1123: Log.getLogger().warning(
1124: "QueryBuilderGraphFrame: cannot get info "
1125: + sqle.getLocalizedMessage());
1126: primaryKeys = new ArrayList();
1127: foreignKeyCols = new ArrayList();
1128: }
1129:
1130: int i = 0;
1131:
1132: // Iterate through the column names
1133: // Put a check by any that are included in the Select clause
1134: while (iterator.hasNext() && i < dbColumnNames.size() /* +1 */) {
1135:
1136: String columnName = new String(iterator.next().toString());
1137:
1138: // Mark them all as selected/deselected, based on Select clause
1139: if (selectColumnNames.contains("*")
1140: || selectColumnNames.contains(columnName)) // NOI18N
1141: dbData[i][0] = Boolean.TRUE;
1142: else
1143: dbData[i][0] = Boolean.FALSE;
1144:
1145: // Check if this is primary or foreign key and then attach appropriate icon
1146: // JLabel (String text, Icon image, SwingConstants.LEFT);
1147: // We used to use toUpperCase() during comparison, but drop that now that we are
1148: // canonicalizing table/column names.
1149: if (primaryKeys.contains(columnName.trim()))
1150: dbData[i][1] = new ImageIcon(url_primary_key);
1151: else if (foreignKeyCols.contains(columnName.trim()))
1152: dbData[i][1] = new ImageIcon(url_foreign_key);
1153: else
1154: dbData[i][1] = null;
1155:
1156: dbData[i][2] = columnName;
1157: _queryBuilderInputTable.addRow(tableSpec, columnName);
1158: _queryBuilderInputTable.selectColumn(tableSpec, columnName,
1159: (Boolean) dbData[i][0]);
1160: i++;
1161: }
1162:
1163: // Create a model from the column info, and wrap it in a frame
1164: QueryBuilderTableModel qbtModel = new QueryBuilderTableModel(
1165: fullTableName, corrName, columnNames, dbData);
1166: qbtModel.addTableModelListener(this );
1167:
1168: // This action is now incorporated into the initialization loop above
1169: // We end up doing it again in order to propagate the events
1170: // Update the internalframe. By event propagation, will update the input table.
1171: // ToDo: Replace this with an explicit update to the InputTableModel.
1172: // This also sets all db columns to display, which is the wrong thing!!!
1173: // We need some way to update the input table. fireTableDataChanged will raise
1174: // an event, but the listener doesn't handle it right. Instead, explicitly update
1175: // the table.
1176: // Add one row to the input table for each column that appears in this node.
1177: // The current code for adding rows is designed to support interactive mouse clicks,
1178: // in tableChanged. We may want to break that out into a separate routine for
1179: // calling from here.
1180: for (i = 0; i < qbtModel.getRowCount(); i++) {
1181: qbtModel.setValueAt(dbData[i][0], i, 0);
1182: }
1183:
1184: String title = (corrName == null) ? tableName : corrName + ": "
1185: + tableName; // NOI18N
1186:
1187: // This is specific to the JGraph implementation, now replaced by GraphLib
1188: // QueryBuilderInternalFrame internalFrame = new QueryBuilderInternalFrame(qbtModel, _queryBuilder);
1189: // internalFrame.addKeyListener(this);
1190:
1191: // // Set the various contents of the internal frame
1192: // internalFrame.create();
1193: // internalFrame.setTitle(title);
1194:
1195: // TestGraph
1196: // Do some extra work to create a node in the new graphlib
1197: Widget widget = _scene.addNode(title, qbtModel);
1198: widget.getActions().addAction(
1199: ActionFactory.createPopupMenuAction(this ));
1200: QBNodeComponent qbNC = findComponent(widget);
1201: widget.setPreferredLocation(getNextGraphLocation(qbNC
1202: .getPreferredSize()));
1203: _scene.validate();
1204:
1205: return qbNC;
1206: }
1207:
1208: /**
1209: * Returns the next location for placing a node.
1210: * Right now it uses a standard offset from the previous location.
1211: */
1212: Point _gLocation;
1213:
1214: Point getNextGraphLocation(Dimension size) {
1215:
1216: Log.getLogger().entering("QueryBuilderGraphFrame",
1217: "getNextGraphLocation",
1218: new Object[] { size.getWidth(), size.getHeight() }); // NOI18N
1219:
1220: if (!_firstTableInserted) {
1221: _gLocation = new Point(initX, initY);
1222: } else {
1223: _gLocation = new Point((int) (_gLocation.getX()
1224: + size.getWidth() + offsetX + randomVal
1225: .nextInt((int) size.getWidth() / 2)),
1226: (int) (_gLocation.getY() + offsetY + randomVal
1227: .nextInt((int) size.getHeight() / 2)));
1228: offsetY *= -1;
1229: if (_gLocation.getX() > (int) size.getWidth()
1230: * MAX_TABLES_IN_A_ROW) {
1231: _gLocation = new Point(initX, (int) _gLocation.getY()
1232: + (int) size.getHeight() + 2 * offsetY);
1233: }
1234: }
1235: return _gLocation;
1236: }
1237:
1238: // Graph: return the NodeComponent associated with this Widget
1239: QBNodeComponent findComponent(Widget widget) {
1240: List<Widget> widgets = widget.getChildren();
1241: for (Widget w : widgets) {
1242: if (w instanceof ComponentWidget)
1243: return (QBNodeComponent) ((ComponentWidget) w)
1244: .getComponent();
1245: }
1246: return null;
1247: }
1248:
1249: // Insert edges that are implied by Foreign Key constraints between existing nodes
1250: // and the new one. Return the list of FKs that are used.
1251: // ToDo: Enable support for multiple edges between a pair of nodes
1252:
1253: private List insertFKEdges(QBNodeComponent newNode,
1254: String newFullTableName) {
1255:
1256: Log.getLogger().entering("QueryBuilderGraphFrame",
1257: "insertFKEdges",
1258: new Object[] { newNode, newFullTableName }); // NOI18N
1259:
1260: // Get foreign key information, for deciding relationship status
1261: // Do this once, to avoid repeated calls to database
1262: List foreignKeys;
1263: try {
1264: foreignKeys = _queryBuilder
1265: .getForeignKeys(newFullTableName);
1266: } catch (SQLException sqle) {
1267: foreignKeys = new ArrayList();
1268: }
1269: List foreignKeysUsed = new ArrayList();
1270:
1271: // TestGraph
1272: // Iterate through the nodes in the scene, drawing arcs to the new node as needed
1273:
1274: Collection nodes = _scene.getNodes();
1275: // Iterate through nodes
1276: for (Object oldNode : nodes) {
1277:
1278: // Check whether this node is related to the new one
1279: String[] fk;
1280: if (oldNode instanceof QBNodeComponent) // Make sure it's not a dummy
1281: // && (root != newCell)) // how to check this?
1282: {
1283: String oldFullTableName = ((QBNodeComponent) oldNode)
1284: .getFullTableName();
1285: if ((fk = _queryBuilder
1286: .findForeignKey(oldFullTableName,
1287: newFullTableName, foreignKeys)) != null)
1288: // ... and is related to the new cell by an FK?
1289: {
1290: String oldTableSpec = ((QBNodeComponent) oldNode)
1291: .getTableSpec();
1292: String newTableSpec = ((QBNodeComponent) newNode)
1293: .getTableSpec();
1294: // Insert an edge between the existing cell and the new one
1295: // Directionality (old -> new) is just a convention, since we make the choice
1296: // fix for 6270428 : querybuilder Issues with SELF JOIN
1297: // having self referential integrity (Employee-Manager Scenario)
1298: if (fk[0].equals(fk[2])) {
1299: fk[0] = oldTableSpec;
1300: fk[2] = newTableSpec;
1301: }
1302: // Default to INNER
1303: insertGraphEdge((QBNodeComponent) oldNode, newNode,
1304: oldFullTableName, newFullTableName,
1305: oldTableSpec, newTableSpec, null, null, fk,
1306: "INNER");
1307: foreignKeysUsed.add(fk);
1308: }
1309: }
1310: }
1311:
1312: return foreignKeysUsed;
1313: }
1314:
1315: // Insert edges connected to this table, which are implied by the join conditions
1316: // associate with the table in the FROM list
1317: void insertJoinEdges(JoinTable joinTable) {
1318:
1319: Log.getLogger().entering("QueryBuilderGraphFrame",
1320: "insertJoinEdges", joinTable); // NOI18N
1321:
1322: // We're generating the graph from the model.
1323: // Don't look for new edges to add, but add the edges that are
1324: // explicitly mentioned in the join condition for this table
1325: String joinType = joinTable.getJoinType();
1326:
1327: // We don't have edges if either
1328: // - this is the first table
1329: // - this is a cross join
1330: // Otherwise, there will be an edge
1331: // Extended 10/22 to support Conditions consisting of conjoined predicates
1332:
1333: if ((joinType != null) && (!joinType.equals("CROSS"))) { // NOI18N
1334:
1335: Expression cond = joinTable.getExpression();
1336: if (cond instanceof Predicate) {
1337: Predicate pred = (Predicate) cond;
1338: insertJoinEdge(pred, joinType);
1339: }
1340: }
1341: }
1342:
1343: // Insert the graph edge corrsponding to this predicate
1344: void insertJoinEdge(Predicate pred, String joinType) {
1345:
1346: Log.getLogger().entering("QueryBuilderGraphFrame",
1347: "insertJoinEdge", new Object[] { pred, joinType }); // NOI18N
1348:
1349: Value val1 = pred.getVal1();
1350: Value val2 = pred.getVal2();
1351:
1352: // Only insert an edge if we're comparing columns, rather than literals
1353: if ((val1 instanceof Column) && (val2 instanceof Column)) {
1354:
1355: Column col1 = (Column) val1;
1356: String tableSpec1 = col1.getTableSpec();
1357: String fullTableName1 = _queryBuilder.getQueryModel()
1358: .getFullTableName(tableSpec1);
1359: String colName1 = col1.getColumnName();
1360: QBNodeComponent node1 = findGraphNode(tableSpec1);
1361:
1362: Column col2 = (Column) val2;
1363: String tableSpec2 = col2.getTableSpec();
1364: String fullTableName2 = _queryBuilder.getQueryModel()
1365: .getFullTableName(tableSpec2);
1366: String colName2 = col2.getColumnName();
1367: QBNodeComponent node2 = findGraphNode(tableSpec2);
1368:
1369: // See if there's a foreign key on exactly this combination of tables/columns
1370: // If not, we will have no direction label on the join
1371: String[] fk = null;
1372: try {
1373: fk = _queryBuilder.findForeignKey(fullTableName1,
1374: colName1, fullTableName2, colName2);
1375: } catch (SQLException sqle) {
1376: Log.getLogger().warning(
1377: "QBGF: findforeignKey "
1378: + sqle.getLocalizedMessage());
1379: }
1380:
1381: // Direction is cell1 -> cell2, matching the join
1382: // tableName is the table that we are adding
1383: insertGraphEdge(node1, node2, fullTableName1,
1384: fullTableName2, tableSpec1, tableSpec2, colName1,
1385: colName2, fk, joinType);
1386: }
1387: }
1388:
1389: // Insert an edge
1390: // Like insertEdge, but uses graphLib
1391: void insertGraphEdge(QBNodeComponent node1, QBNodeComponent node2,
1392: String fullTableName1, String fullTableName2,
1393: String tableSpec1, String tableSpec2, String colName1,
1394: String colName2, String[] fk, String joinType) {
1395: System.out.println("Entering insertGraphEdge, tableSpec1: "
1396: + tableSpec1 + " tableSpec2: " + tableSpec2);
1397:
1398: // Create the node object for the edge (for the Property Sheet)
1399: AbstractNode an;
1400: if (colName1 == null) {
1401: // We are adding the edge interactively, based on the FK
1402: // Use the columns specified in the FK
1403: if (fullTableName1.equalsIgnoreCase(fk[0]))
1404: an = createPropertyNode(tableSpec1, fk[1], tableSpec2,
1405: fk[3], joinType, _queryBuilder); // NOI18N
1406: else
1407: an = createPropertyNode(tableSpec2, fk[1], tableSpec1,
1408: fk[3], joinType, _queryBuilder); // NOI18N
1409: } else {
1410: // Adding an edge from the model, based on join condition
1411: an = createPropertyNode(tableSpec1, colName1, tableSpec2,
1412: colName2, joinType, _queryBuilder); // NOI18N
1413: }
1414:
1415: // Now, add an edge to the graph scene, containing the AbstractNode
1416: Widget widget = _scene.addEdge(an);
1417: _scene.setEdgeSource(an, node1);
1418: _scene.setEdgeTarget(an, node2);
1419:
1420: ConnectionWidget connWidget = (ConnectionWidget) widget;
1421:
1422: // widget.getActions().addAction(ActionFactory.createSelectAction(new ObjectSelectProvider()));
1423: connWidget.getActions().addAction(_scene.createSelectAction());
1424:
1425: AnchorShape triangle = AnchorShapeFactory
1426: .createTriangleAnchorShape(18, true, false, 17);
1427: connWidget.setStroke(new BasicStroke(1.5f,
1428: BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
1429: // If we have an FK, draw the appropriate arrowhead, otherwise don't do anything
1430: if (fk != null) {
1431: if (fk[0].equalsIgnoreCase(fullTableName1)) {
1432: // ((ConnectionWidget)widget).setTargetAnchorShape (AnchorShape.TRIANGLE_FILLED);
1433: connWidget.setTargetAnchorShape(triangle);
1434: } else {
1435: connWidget.setSourceAnchorShape(triangle);
1436: }
1437: }
1438:
1439: // Mark the edge as activated (updates property sheet)
1440: _queryBuilder.setActivatedNodes(new Node[] { an });
1441:
1442: _scene.validate();
1443: }
1444:
1445: AbstractNode createPropertyNode(String tableSpec1, String fk1,
1446: String tableSpec2, String fk3, String joinType,
1447: QueryBuilder _queryBuilder) {
1448: if ((joinType == null) || (joinType.equals("")))
1449: return new CondNode(tableSpec1, fk1, tableSpec2, fk3,
1450: _queryBuilder);
1451: else
1452: return new JoinNode(tableSpec1, fk1, tableSpec2, fk3,
1453: joinType, _queryBuilder);
1454: }
1455:
1456: /**
1457: * Remove a table (node) from the graph
1458: */
1459: void removeTable(QBNodeComponent selectedNode) {
1460:
1461: String tableSpec = selectedNode.getTableSpec();
1462:
1463: Log.getLogger().entering("QueryBuilderGraphFrame",
1464: "removeTable", tableSpec); // NOI18N
1465:
1466: // Remove the specified node from the GraphLib scene
1467: // First remove all edges
1468: Collection edges = _scene.findNodeEdges(selectedNode, true,
1469: true);
1470: for (Object edge : edges) {
1471: // Edge is a JoinNode or CondNode
1472: _scene.removeEdge(edge);
1473: }
1474:
1475: // Now remove the node
1476: _scene.removeNode(selectedNode);
1477:
1478: // Update the input table model, by removing all rows that mention this table
1479: _queryBuilderInputTable.removeRows(tableSpec);
1480:
1481: // Now update the QueryModel
1482: // Note that we always do this, since the model-driven graph generation never contain deletion
1483: _queryBuilder.getQueryModel().removeTable(tableSpec);
1484:
1485: _queryBuilder.generate();
1486:
1487: // update the groupby checkbox menu item.
1488: setGroupBy(_queryBuilder.getQueryModel().hasGroupBy());
1489: }
1490:
1491: // Generate the Graph and Table from a query model
1492: // General algorithm:
1493: // - Select clause defines the internal frame (node for each Table)
1494: // - From clause defines the structure of the Graph
1495: // - Where clause has no effect on Graph, but does affect the tabular version
1496: // - Group by, Having, Order by are not shown in the Graph at all
1497: // Select clause entries in the table are filled in as a side effect during createNode
1498:
1499: void generateGraph(QueryModel query) {
1500:
1501: Log.getLogger().entering("QueryBuilderGraphFrame",
1502: "generateGraph");
1503:
1504: if (_disableQBGF)
1505: return;
1506:
1507: // Reset the graph and input table
1508: // This is also done in QBP.clear()
1509: // ToDo: Decide whether we need to restore this
1510: // clearGraph();
1511:
1512: _queryBuilder._updateModel = false;
1513: try {
1514: generateGraphFrom(query);
1515: generateGraphWhere(query);
1516: generateGraphOrderBy(query);
1517: refresh();
1518: } catch (Exception e) {
1519: e.printStackTrace();
1520: } finally {
1521: _queryBuilder._updateModel = true;
1522: }
1523: }
1524:
1525: // Reset the graph and input table to an empty state
1526: // This could be implemented in various ways; either retain the same data structure
1527: // but explicitly empty it, or just create a new instance. We favor explicit emptying,
1528: // except for the graph model
1529: // No longer used, since we just re-create the scene
1530: // synchronized void clearGraph() {
1531: //
1532: // Log.getLogger().entering("QueryBuilderGraphFrame", "clearGraph"); // NOI18N
1533: // // This is used in certain places
1534: // _firstTableInserted=false;
1535: //
1536: // // Clear the InputTableModel
1537: // _inputTableModel.setRowCount(0);
1538: //
1539: // // Clear the scene, by removing each component
1540: // Collection nodes = _scene.getNodes();
1541: // for (Object node : nodes) {
1542: // System.out.println("Removing Node: " + node + ((QBNodeComponent)node).getNodeName());
1543: // _scene.removeNodeWithEdges(node);
1544: // }
1545: //// Collection edges = _scene.getEdges();
1546: //// for (Object edge : edges) {
1547: //// _scene.removeEdge(edge);
1548: //// System.out.println("Removing Edge: " + edge);
1549: //// }
1550: // }
1551:
1552: // Generate the graph corresponding to the FROM clause.
1553: // Also updates the table entries corresponding to the SELECT clause;
1554: // that should be refactored.
1555:
1556: private void generateGraphFrom(QueryModel query) {
1557:
1558: Log.getLogger().entering("QueryBuilderGraphFrame",
1559: "generateGraphFrom"); // NOI18N
1560:
1561: // Start with the From clause
1562: List tables = query.getFrom().getTableList();
1563:
1564: // Iterate through the list of tables, adding them to the graph
1565: for (int i = 0; i < tables.size(); i++) {
1566:
1567: JoinTable joinTable = (JoinTable) tables.get(i);
1568: String tableSpec = joinTable.getTableSpec();
1569: String fullTableName = joinTable.getFullTableName();
1570:
1571: // We also need to use the SELECT list to set the columns in the
1572: // interior of the table node. We could pass in the whole FROM list,
1573: // and scan in with each table, or we could partition it once, and
1574: // pass in the relevant entries with each table. If we go the
1575: // partitioning approach, we could revise the model to use it.
1576: List columnNames = new ArrayList();
1577: query.getColumnNames(tableSpec, columnNames);
1578: insertTableFromModel(joinTable, columnNames);
1579: }
1580: }
1581:
1582: // check if the where clause has any duplicate columns
1583: // e.g. if the where caluse is like "where table.c > 10 AND table.c < 20"
1584: // then this will return true. Current decisoin is not to display multiple
1585: // columns in the grid pane.
1586: private boolean whereHasDuplicateColumns(List whereColumns,
1587: Predicate expr) {
1588: List columns = new ArrayList();
1589: expr.getReferencedColumns(columns);
1590: for (int i = 0; i < columns.size(); i++) {
1591: if (DEBUG) {
1592: System.out
1593: .println("expr getReferencedColumn i = "
1594: + i
1595: + " "
1596: + ((Column) columns.get(i))
1597: .genText(null));
1598: }
1599:
1600: Column c = (Column) columns.get(i);
1601: int found = 0;
1602: for (int j = 0; j < whereColumns.size(); j++) {
1603: if (((Column) whereColumns.get(j)).equals(c)) {
1604: found++;
1605: if (DEBUG) {
1606: System.out
1607: .println("where getReferencedColumn j = "
1608: + j
1609: + " "
1610: + " found = "
1611: + found
1612: + ((Column) whereColumns.get(j))
1613: .genText(null));
1614: }
1615: }
1616: if (found > 1) // more than one instance
1617: return true;
1618: }
1619: }
1620: return false;
1621: }
1622:
1623: // Generate the Graph and Table entries corresponding to the WHERE clause
1624: // Graph: edges Table: criteria
1625:
1626: private void generateGraphWhere(QueryModel query) {
1627:
1628: Log.getLogger().entering("QueryBuilderGraphFrame",
1629: "generateGraphWhere"); // NOI18N
1630:
1631: // if this is true we should not handle any events in tableChanged.
1632: _inputTableAddCriteria = true;
1633:
1634: // Now handle the WHERE clause
1635: // For now, assume a single predicate, possibly parameterized
1636: // Each predicate becomes a row in the InputTable, with an appte entry in Criteria
1637: // Example code for adding rows to the table is in tableChanged
1638: // For each predicate (col = [ value | ? ] )
1639: // ToDo: handle all values, not just *, ?
1640: // ToDo: introduce a different class for join predicates
1641: Where where = query.getWhere();
1642: if (where != null) {
1643:
1644: Expression expr = where.getExpression();
1645:
1646: List whereColumns = new ArrayList();
1647: where.getReferencedColumns(whereColumns);
1648:
1649: if (expr == null) {
1650: _inputTableAddCriteria = false;
1651: return;
1652: } else if (expr instanceof Predicate) {
1653: if (whereHasDuplicateColumns(whereColumns,
1654: (Predicate) expr)) // more than one instance
1655: insertPredicate((Predicate) expr, -1);
1656: else
1657: insertPredicate((Predicate) expr, 0);
1658: } else if (expr instanceof And) {
1659: insertAndOr(expr, whereColumns, 0);
1660: } else if (expr instanceof Or) {
1661: insertAndOr(expr, whereColumns, 0);
1662: }
1663: }
1664: _inputTableAddCriteria = false;
1665: }
1666:
1667: // insert AND / OR expression in the grid pane. if a column appears
1668: // more than once in the where clause then we display the "****" in the
1669: // criteria column and the order is displayed as "*".
1670: private void insertAndOr(Expression expr, List whereColumns,
1671: int order) {
1672: ExpressionList exprList = (ExpressionList) expr;
1673: for (int i = 0; i < exprList.size(); i++) {
1674: expr = exprList.getExpression(i);
1675: if (expr instanceof Predicate) {
1676: if (whereHasDuplicateColumns(whereColumns,
1677: (Predicate) expr))
1678: // more than one instance
1679: insertPredicate((Predicate) expr, -1);
1680: else
1681: insertPredicate((Predicate) expr, order++);
1682: } else if (expr instanceof And) {
1683: insertAndOr(expr, whereColumns, order++);
1684: } else if (expr instanceof Or) {
1685: insertAndOr(expr, whereColumns, order++);
1686: }
1687: }
1688: }
1689:
1690: /**
1691: * Insert the predicate into visual editor, either as a graph edge (for
1692: * a relationship) or as a table entry (for a criterion).
1693: * Order is the initial value for the criteria order.
1694: * if order is -1, then add "*****" to criteria column and "*" to
1695: * order column and make both of them uneditable.
1696: */
1697: private void insertPredicate(Predicate pred, int order) {
1698:
1699: Value val1 = pred.getVal1();
1700: Value val2 = pred.getVal2();
1701:
1702: if ((val1 instanceof Column) && (val2 instanceof Column)) {
1703: // Comparing two columns -- insert an edge
1704: insertJoinEdge(pred, "");
1705: } else {
1706: // Assume that the right hand side is a literal value
1707: // This will result in an entry into the InputTable
1708: String marker = pred.getVal2().toString();
1709:
1710: // Treat this like any other literal value now
1711: // if (marker.equals("?")) {}
1712:
1713: // We can only count on the tableSpec; tableName might contain corrName
1714: Column col = (Column) val1;
1715: String tableSpec = col.getTableSpec();
1716: String columnName = col.getColumnName();
1717:
1718: // Create the value that we're going to put into the table
1719: String val = pred.getOp() + " " + marker; // NOI18N
1720:
1721: // If we're inserting a criterion, get order. -1 is a special case, meaning ""
1722: if (order == -1) {
1723: _queryBuilderInputTable
1724: .addCriterion(
1725: tableSpec,
1726: columnName,
1727: QueryBuilderInputTable.Criteria_Uneditable_String,
1728: QueryBuilderInputTable.CriteriaOrder_Uneditable_String);
1729: } else {
1730: String orderString = (order == -1) ? "" : new Integer(
1731: order + 1).toString();
1732:
1733: // Update the appropriate row, or add a new one
1734: _queryBuilderInputTable.addCriterion(tableSpec,
1735: columnName, val, orderString);
1736: }
1737: }
1738: }
1739:
1740: // Update the InputTable to show the OrderBy specs
1741:
1742: private void generateGraphOrderBy(QueryModel query) {
1743:
1744: // Delegate this to the InputTable, which is the only component affected
1745: _queryBuilderInputTable.generateTableOrderBy(query);
1746: }
1747:
1748: /**
1749: * Finds a graph node with the specified name (tableSpec)
1750: */
1751: QBNodeComponent findGraphNode(String tableSpec) {
1752:
1753: Log.getLogger().entering("QueryBuilderGraphFrame",
1754: "findGraphNode", tableSpec); // NOI18N
1755:
1756: Collection nodes = _scene.getNodes();
1757: for (Object node : nodes) {
1758: if ((node instanceof QBNodeComponent)
1759: && ((QBNodeComponent) node).getTableSpec().equals(
1760: tableSpec))
1761: return (QBNodeComponent) node;
1762: }
1763:
1764: return null;
1765: }
1766:
1767: // Top-level method for adding a table to the query.
1768: // Called from the "Add Table" menu item
1769: public void addTable() {
1770: Log.getLogger().entering("QueryBuilderGraphFrame", "addTable"); // NOI18N
1771:
1772: // if ( _queryBuilder.checkDatabaseAndDisable(null) == false ) return;
1773:
1774: QueryBuilder.showBusyCursor(true);
1775: try {
1776:
1777: List tableNames = _queryBuilder.getAllTables();
1778: String[] tableStrings = new String[tableNames.size()];
1779: tableNames.toArray(tableStrings);
1780: _addTableDlg = new AddTableDlg(tableStrings, true);
1781: if (DEBUG)
1782: System.out
1783: .println("Database tablenames: " + tableNames); // NOI18N
1784:
1785: // It's not clear why we need to do this here, since we passed
1786: // tables into the constructor
1787: // _addTableDlg.setTableValues(_tableStrings) ;
1788: if (_addTableDlg.getReturnStatus() == 1) {
1789: // <change/> Moving to NB winsys.
1790: // WindowManager.getDefault().showBusyCursor(true);
1791: SwingUtilities.invokeLater(new Runnable() {
1792: public void run() {
1793: Object[] selectedTables = _addTableDlg
1794: .getSelectedValues();
1795: // refresh();
1796: // QueryBuilder.showBusyCursor ( true );
1797: for (int i = 0; i < selectedTables.length; i++) {
1798: final String finalVal = (String) selectedTables[i];
1799: insertTableInteractively(finalVal);
1800: }
1801: _queryBuilder.generateText();
1802: runQueryMenuItem.setEnabled(true);
1803: groupByMenuItem.setEnabled(true);
1804: _queryBuilder.getQueryBuilderPane()
1805: .getQueryBuilderSqlTextArea()
1806: .setRunQueryMenuEnabled(true);
1807: _queryBuilder.getQueryBuilderPane()
1808: .getQueryBuilderSqlTextArea()
1809: .setParseQueryMenuEnabled(true);
1810: // QueryBuilder.showBusyCursor ( false );
1811: }
1812: });
1813: // <change/> Moving to NB winsys.
1814: // WindowManager.getDefault().showBusyCursor(false);
1815: }
1816: // somehow the graph still thinks we are not changed enough to redraw.
1817: // this causes the edges not to get drawn, as well as the scroll bars
1818: // not getting updated.
1819: // QueryBuilderInternalFrame currentSelectedFrame =
1820: // (QueryBuilderInternalFrame)_desktopPane.getSelectedFrame();
1821: // if ( currentSelectedFrame != null ) {
1822: // redrawFrameWithMove( currentSelectedFrame );
1823: // }
1824: } catch (SQLException sqe) {
1825: // JDTODO - We need a consistent approach to handling SQL Exceptions. Best is probably to expose them to user.
1826: // This comes up a number of places in QBGF.
1827: // _queryBuilder.checkDatabaseAndDisable(null) ;
1828: } finally {
1829: QueryBuilder.showBusyCursor(false);
1830: }
1831: }
1832:
1833: /**
1834: * Responds to a menu selection
1835: *
1836: * Current choices are "Add Table", "Run Query", "Remove From Query"
1837: */
1838: public void actionPerformed(ActionEvent e) {
1839:
1840: Log.getLogger().entering("QueryBuilderGraphFrame",
1841: "actionPerformed"); //NOI18N
1842:
1843: JMenuItem source = (JMenuItem) (e.getSource());
1844:
1845: if (source.getText().equals(
1846: NbBundle.getMessage(QueryBuilderGraphFrame.class,
1847: "Add_Table"))) { // NOI18N
1848: addTable();
1849: }
1850:
1851: else if (source.getText().equals(
1852: NbBundle.getMessage(QueryBuilderGraphFrame.class,
1853: "RUN_QUERY"))) // NOI18N
1854: {
1855: // Execute the query
1856: _queryBuilder.executeQuery(_sqlTextArea.getText());
1857: }
1858:
1859: else if (source.getText().equals(
1860: NbBundle.getMessage(QueryBuilderGraphFrame.class,
1861: "REMOVE_FROM_QUERY"))) { // NOI18N
1862: removeTable();
1863: }
1864: }
1865:
1866: // public boolean isSelectionEmpty() {
1867: // // if there are no tables, selection should be empty.
1868: // if ( _firstTableInserted == false ) return true;
1869: // QueryBuilderInternalFrame currentSelectedFrame =
1870: // (QueryBuilderInternalFrame)_desktopPane.getSelectedFrame();
1871: // return ( currentSelectedFrame == null );
1872: // }
1873:
1874: // Remove the table node from the graph (and model)
1875: // update delete menu
1876: public void removeNode(TableNode node) {
1877: // QueryBuilder.showBusyCursor( true );
1878:
1879: // // remove the selected table to fix
1880: // // 6253516 : "delete" key doesn't work for same tables in QE.
1881: // removeTable();
1882: // QueryBuilder.showBusyCursor( false );
1883: }
1884:
1885: // Remove the condition node from the graph (and model)
1886: // update delete menu
1887: public void removeNode(CondNode node) {
1888: // QueryBuilder.showBusyCursor( true );
1889: // String[] rel = new String[4];
1890: // rel[0] = node.getTable1();
1891: // rel[1] = node.getColumn1();
1892: // rel[2] = node.getTable2();
1893: // rel[3] = node.getColumn2();
1894:
1895: // if (DEBUG) {
1896: // System.out.println(" QBGF.removeNode() table1 = " + rel[0] + " column1 = " + rel[1] + " table2 = " + rel[2] + " column2 = " + rel[3] + "\n" ); // NOI18N
1897: // }
1898:
1899: // Predicate pred = SQLQueryFactory.createPredicate(rel);
1900: // _queryBuilder._updateText=false;
1901: // _queryBuilder.getQueryModel().removeCondition( pred );
1902: // Object cell = _graph.getSelectionCell();
1903: // if (cell instanceof DefaultEdge) {
1904: // _graphModel.remove(new Object[] {cell});
1905: // }
1906: // _queryBuilder._updateText=true;
1907: // _queryBuilder.generateText();
1908: // _queryBuilder.activateActions();
1909: // QueryBuilder.showBusyCursor( false );
1910: }
1911:
1912: // Remove the join node from the graph (and model)
1913: // update delete menu
1914: public void removeNode(JoinNode node) {
1915: // QueryBuilder.showBusyCursor( true );
1916: // String[] rel = new String[4];
1917: // rel[0] = node.getTable1();
1918: // rel[1] = node.getColumn1();
1919: // rel[2] = node.getTable2();
1920: // rel[3] = node.getColumn2();
1921:
1922: // if (DEBUG) {
1923: // System.out.println(" QBGF.removeNode() table1 = " + rel[0] + " column1 = " + rel[1] + " table2 = " + rel[2] + " column2 = " + rel[3] + "\n" ); // NOI18N
1924: // }
1925:
1926: // _queryBuilder._updateText=false;
1927: // _queryBuilder.getQueryModel().removeJoinNode( rel[0], rel[1], rel[2], rel[3] );
1928: // Object cell = _graph.getSelectionCell();
1929: // if (cell instanceof DefaultEdge) {
1930: // _graphModel.remove(new Object[] {cell});
1931: // }
1932: // _queryBuilder._updateText=true;
1933: // _queryBuilder.generateText();
1934: // _queryBuilder.activateActions();
1935: // QueryBuilder.showBusyCursor( false );
1936: }
1937:
1938: // Remove the selected frame from the graph (and model)
1939: // update delete menu
1940: public void removeTable() {
1941:
1942: Log.getLogger().entering("QueryBuilderGraphFrame",
1943: "removeTable"); //NOI18N
1944:
1945: QueryBuilder.showBusyCursor(true);
1946: // Important: suppress bogus regeneration of the text query
1947: _queryBuilder._updateText = false;
1948: try {
1949: removeTable(_selectedNode);
1950: } finally {
1951: _queryBuilder._updateText = true;
1952: }
1953:
1954: // enable/disable group_by menu item
1955: if (_sqlTextArea.getText() == null) {
1956: runQueryMenuItem.setEnabled(false);
1957: groupByMenuItem.setEnabled(false);
1958: _queryBuilder.getQueryBuilderPane()
1959: .getQueryBuilderSqlTextArea()
1960: .setRunQueryMenuEnabled(false);
1961: _queryBuilder.getQueryBuilderPane()
1962: .getQueryBuilderSqlTextArea()
1963: .setParseQueryMenuEnabled(false);
1964: } else if (_sqlTextArea.getText().trim().length() == 0) {
1965: runQueryMenuItem.setEnabled(false);
1966: groupByMenuItem.setEnabled(false);
1967: _queryBuilder.getQueryBuilderPane()
1968: .getQueryBuilderSqlTextArea()
1969: .setRunQueryMenuEnabled(false);
1970: _queryBuilder.getQueryBuilderPane()
1971: .getQueryBuilderSqlTextArea()
1972: .setParseQueryMenuEnabled(false);
1973: } else {
1974: runQueryMenuItem.setEnabled(true);
1975: groupByMenuItem.setEnabled(true);
1976: _queryBuilder.getQueryBuilderPane()
1977: .getQueryBuilderSqlTextArea()
1978: .setRunQueryMenuEnabled(true);
1979: _queryBuilder.getQueryBuilderPane()
1980: .getQueryBuilderSqlTextArea()
1981: .setParseQueryMenuEnabled(true);
1982: }
1983: QueryBuilder.showBusyCursor(false);
1984: }
1985:
1986: // Responds to a CheckBoxMenuItem -- Group by
1987:
1988: public void itemStateChanged(ItemEvent e) {
1989:
1990: JMenuItem source = (JMenuItem) (e.getSource());
1991:
1992: if (source.getText().equals(
1993: NbBundle.getMessage(QueryBuilderGraphFrame.class,
1994: "GROUP_BY"))) { // NOI18N
1995:
1996: // Add or remove a Group by clause
1997: if (e.getStateChange() == ItemEvent.SELECTED) {
1998: // Add a Group By to the model
1999: _queryBuilder.getQueryModel().addGroupBy();
2000: } else {
2001: // Remove a Group By from the model
2002: _queryBuilder.getQueryModel().removeGroupBy();
2003: }
2004: _queryBuilder.generateText();
2005: }
2006: }
2007:
2008: // Set the checkbox in the GroupBy menu item
2009: public void setGroupBy(boolean b) {
2010: groupByMenuItem.setSelected(b);
2011: }
2012:
2013: // somehow the graph still thinks we are not changed enough to redraw.
2014: // this causes the edges not to get drawn, as well as the scroll bars
2015: // not getting updated.
2016: void redrawFrameWithMove(QueryBuilderInternalFrame frame) {
2017: // if (frame != null)
2018: // // && frame.isShowing()
2019: // {
2020: // HashMap map = new HashMap();
2021: // Map atts = GraphConstants.createMap();
2022:
2023: // GraphConstants.setBounds(atts,frame.getBounds());
2024: // map.put(frame.getGraphCell(),atts);
2025: // // Update the graph model with the new attributes, which include frame bounds(?)
2026: // _graphModel.edit(map,null,null,null);
2027: // }
2028:
2029: // // the 2 lines below need to be there for this to work
2030: // // but they should not be there in redrawFrame.
2031: // // That is the only difference between redrawFrame and
2032: // // redrawFrameWithMove
2033: // _desktopPane.setBounds(_canvas.getBounds());
2034: // _desktopPane.updateUI();
2035:
2036: // resizeDesktop();
2037: // _queryBuilder.getQueryBuilderPane().getQueryBuilderSqlTextArea().requestFocus(true);
2038: }
2039:
2040: // Redraw an internal frame?
2041:
2042: private void redrawFrame(QueryBuilderInternalFrame frame) {
2043: // if (frame != null)
2044: // // && frame.isShowing()
2045: // {
2046: // HashMap map = new HashMap();
2047: // Map atts = GraphConstants.createMap();
2048:
2049: // GraphConstants.setBounds(atts,frame.getBounds());
2050: // map.put(frame.getGraphCell(),atts);
2051: // // Update the graph model with the new attributes, which include frame bounds(?)
2052: // _graphModel.edit(map,null,null,null);
2053: // }
2054: }
2055:
2056: // Manually refresh the graph display -- copied from ComponentListener below
2057: // The forum suggested something like graphDidChange(); revalidate(); but not tried
2058: // ToDo: Decide whether we need something like this with GraphLib
2059: void refresh() {
2060: // _graph.graphDidChange();
2061: // _graph.revalidate();
2062: // if (DEBUG) {
2063: // System.out.println(" refresh() called " + "\n" ); // NOI18N
2064: // System.out.println(" width = " + _graph.getSize().getWidth() + " Height = " + _graph.getSize().getHeight() + "\n" ); // NOI18N
2065: // }
2066: // resizeDesktop();
2067: }
2068:
2069: // Inner classes, mostly for Listeners
2070:
2071: // A listener that will bring up the background menu
2072:
2073: // class BackgroundPopupListener extends MouseAdapter {
2074:
2075: // public void mousePressed(MouseEvent e) {
2076: // maybeShowPopup(e);
2077: // }
2078:
2079: // public void mouseReleased(MouseEvent e) {
2080: // maybeShowPopup(e);
2081: // }
2082:
2083: // private void maybeShowPopup(MouseEvent e) {
2084: // if ( _disableQBGF ) return;
2085: // if (e.isPopupTrigger() ) {
2086: // Object cell = _graph.getFirstCellForLocation(e.getX(), e.getY());
2087: // if ( ( cell != null ) && ( cell instanceof DefaultEdge ) ) {
2088: // // do not show popup.
2089: // // bug 4979403 right click and double click the Join edge
2090: // return;
2091: // }
2092: // if (e.getComponent().isEnabled() )
2093: // _backgroundPopup.show( e.getComponent(), e.getX(), e.getY() );
2094: // }
2095: // }
2096: // }
2097: /**
2098: * An adapter class for receiving internal frame events. Used
2099: * to detect when a Frame (graph node representing a table) is selected
2100: */
2101: private class FrameSelectionListener extends InternalFrameAdapter {
2102:
2103: public void internalFrameActivated(InternalFrameEvent ife) {
2104: Object source = (Object) (ife.getSource());
2105:
2106: // When any internal frame is activated, enable these two listeners?
2107: // _apifa.setEnabled(true);
2108: // _acifa.setEnabled(true);
2109:
2110: // Finally, bring up property sheet. Ignore event, just check which frame is selected.
2111: QueryBuilderInternalFrame currentSelectedFrame = (QueryBuilderInternalFrame) _desktopPane
2112: .getSelectedFrame();
2113: setActivatedNode(currentSelectedFrame);
2114: }
2115:
2116: public void internalFrameDeactivated(InternalFrameEvent ife) {
2117: // _apifa.setEnabled(false);
2118: // _acifa.setEnabled(false);
2119: }
2120: }
2121:
2122: // An adapter class for receiving component events
2123: // This listens for events from the desktopPane
2124:
2125: private class CompListener extends ComponentAdapter {
2126:
2127: public void componentResized(ComponentEvent ce) {
2128: if (_disableQBGF) {
2129: return;
2130: } else {
2131: refresh();
2132: }
2133: }
2134: }
2135:
2136: // An adapter class for receiving component events
2137: // This listens for events from nodes (internal frames)
2138:
2139: // private class FrameComponentListener extends ComponentAdapter {
2140:
2141: // // The following two methods are defined on the ComponentListener interface
2142: // public void componentResized(ComponentEvent ce) {
2143: // componentMoved(ce);
2144: // }
2145:
2146: // public void componentMoved(ComponentEvent ce) {
2147: // HashMap map = new HashMap();
2148: // Map atts = GraphConstants.createMap();
2149: // QueryBuilderInternalFrame frame = (QueryBuilderInternalFrame)ce.getComponent();
2150: // refresh();
2151:
2152: // GraphConstants.setBounds(atts,frame.getBounds());
2153: // map.put(frame.getGraphCell(),atts);
2154: // _graphModel.edit(map,null,null,null);
2155:
2156: // resizeDesktop();
2157: // }
2158: // }
2159:
2160: /**
2161: * Listener for detecting changes to the graph selection, to update Property Sheet
2162: *
2163: * This only detects selection for edges (joins); nodes are handled elsewhere.
2164: */
2165: // private class GraphSelListener implements GraphSelectionListener {
2166: // public void valueChanged(GraphSelectionEvent e) {
2167: // Log.getLogger().finest("Graph selection changed, event: "+e); // NOI18N
2168: // if (_graph.getSelectionCount() > 0) {
2169: // // Use the first selection; should only be one
2170: // Object cell = _graph.getSelectionCell();
2171: // if ( ( cell != null ) && ( cell instanceof DefaultEdge ) ) {
2172: // // We've selected an edge. Update the Property Sheet, by setting
2173: // // the activated node to the underlying join
2174: // AbstractNode an = (AbstractNode)(((DefaultEdge) cell).getUserObject());
2175: // _queryBuilder.setActivatedNodes(new Node[] { an });
2176: // QueryBuilderInternalFrame currentSelectedFrame =
2177: // (QueryBuilderInternalFrame) _desktopPane.getSelectedFrame();
2178: // try {
2179: // if ( currentSelectedFrame != null ) {
2180: // currentSelectedFrame.setSelected( false );
2181: // }
2182: // } catch ( java.beans.PropertyVetoException pve ) {
2183: // // do nothing
2184: // }
2185: // }
2186: // }
2187: // }
2188: // }
2189:
2190: // GraphLib class for handling selection
2191: private class ObjectSelectProvider implements SelectProvider {
2192:
2193: public boolean isAimingAllowed(Widget widget,
2194: Point localLocation, boolean invertSelection) {
2195: return false;
2196: }
2197:
2198: public boolean isSelectionAllowed(Widget widget,
2199: Point localLocation, boolean invertSelection) {
2200: return true;
2201: }
2202:
2203: public void select(Widget widget, Point localLocation,
2204: boolean invertSelection) {
2205:
2206: Object object = _scene.findObject(widget);
2207: if ((object != null) && (object instanceof AbstractNode)) {
2208: AbstractNode an = (AbstractNode) object;
2209: _queryBuilder.setActivatedNodes(new Node[] { an });
2210: _scene.userSelectionSuggested(Collections
2211: .singleton(object), invertSelection);
2212: }
2213: }
2214: }
2215:
2216: // Utility class for timing
2217:
2218: public class PerfTimer {
2219:
2220: long _time;
2221:
2222: public PerfTimer() {
2223: resetTimer();
2224: }
2225:
2226: // reset Timer
2227: public void resetTimer() {
2228: // set current time
2229: _time = System.currentTimeMillis();
2230: }
2231:
2232: public long elapsedTime() {
2233: // get elapsed Time
2234: return (System.currentTimeMillis() - _time);
2235: }
2236:
2237: public void print(String aString) {
2238: System.out.println(aString + ": " + this .elapsedTime()
2239: + " ms"); // NOI18N
2240: }
2241: }
2242:
2243: public void dragEnter(DropTargetDragEvent e) {
2244: e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
2245: }
2246:
2247: public void drop(DropTargetDropEvent e) {
2248: if (_disableQBGF)
2249: return;
2250: try {
2251: Transferable tr = e.getTransferable();
2252: DataFlavor[] dataFlavors = tr.getTransferDataFlavors();
2253: for (int i = 0; i < dataFlavors.length; i++) {
2254: Object o = tr.getTransferData(dataFlavors[i]);
2255: if (o instanceof Node) {
2256: e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
2257: // user should be allowed to drop tables from the
2258: // current data source only.
2259: List tableNamesArrayList = _queryBuilder
2260: .getAllTables();
2261: String fullTableName = ((Node) o).getName();
2262:
2263: // Reassign fullTableName to just the table name - minus the schema name
2264: String tableName;
2265: String justTableName;
2266: for (int j = 0; j < tableNamesArrayList.size(); j++) {
2267: tableName = (String) tableNamesArrayList.get(j);
2268: justTableName = (String) tableName.split("\\.")[1];
2269: if (justTableName.equals(fullTableName)) {
2270: fullTableName = tableName;
2271: break;
2272: }
2273: }
2274:
2275: if (tableNamesArrayList.contains(fullTableName)) {
2276: insertTableInteractively(fullTableName);
2277: _queryBuilder.generateText();
2278: runQueryMenuItem.setEnabled(true);
2279: groupByMenuItem.setEnabled(true);
2280: _queryBuilder.getQueryBuilderPane()
2281: .getQueryBuilderSqlTextArea()
2282: .setRunQueryMenuEnabled(true);
2283: _queryBuilder.getQueryBuilderPane()
2284: .getQueryBuilderSqlTextArea()
2285: .setParseQueryMenuEnabled(true);
2286: refresh();
2287: // _graph.getGraphLayoutCache().reload();
2288: resizeDesktop();
2289: // somehow the graph still thinks we are not changed
2290: // enough to redraw. this causes the edges not to get
2291: // drawn, as well as the scroll bars not getting
2292: // updated.
2293:
2294: // 117724 DnD table null pointer exception occurs - removed code to get the currentSelectedFrame
2295: // (QueryBuilderInternalFrame)_desktopPane.getSelectedFrame();
2296: } else {
2297: String msg = NbBundle
2298: .getMessage(
2299: QueryBuilderGraphFrame.class,
2300: "DRAG_AND_DROP_FROM_CURRENT_DATASOURCE");
2301: NotifyDescriptor d = new NotifyDescriptor.Message(
2302: msg
2303: + ", "
2304: + _queryBuilder
2305: .getConnectionInfo()
2306: + "\n\n", // NOI18N
2307: NotifyDescriptor.ERROR_MESSAGE);
2308: DialogDisplayer.getDefault().notify(d);
2309: }
2310: e.dropComplete(true);
2311: return;
2312: }
2313: }
2314: e.rejectDrop();
2315: } catch (Exception ex) {
2316: Log.getLogger().finest(
2317: "Data transfer exception: "
2318: + ex.getLocalizedMessage()); // NOI18N
2319: }
2320: }
2321:
2322: public void dragExit(DropTargetEvent e) {
2323:
2324: }
2325:
2326: public void dragOver(DropTargetDragEvent e) {
2327:
2328: }
2329:
2330: public void dropActionChanged(DropTargetDragEvent e) {
2331:
2332: }
2333:
2334: public void dragOver(DragSourceDragEvent e) {
2335:
2336: }
2337:
2338: public void dropActionChanged(DragSourceDragEvent e) {
2339:
2340: }
2341: }
|