001/* 002 * Copyright (c) 2003-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: welker $' 006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 007 * '$Revision: 24234 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.objectmanager.data.db; 031 032import java.awt.BorderLayout; 033import java.awt.Color; 034 035import javax.swing.JComponent; 036import javax.swing.JLabel; 037import javax.swing.JOptionPane; 038import javax.swing.JScrollPane; 039import javax.swing.event.TableModelEvent; 040import javax.swing.event.TableModelListener; 041 042import org.ecoinformatics.seek.querybuilder.DBQueryDef; 043import org.ecoinformatics.seek.querybuilder.DBQueryDefParserEmitter; 044import org.ecoinformatics.seek.querybuilder.DBSchemaParserEmitter; 045import org.ecoinformatics.seek.querybuilder.QBApp; 046 047import ptolemy.actor.gui.TableauFrame; 048import ptolemy.kernel.util.NamedObj; 049import ptolemy.kernel.util.StringAttribute; 050import ptolemy.moml.MoMLChangeRequest; 051import ptolemy.util.StringUtilities; 052 053////////////////////////////////////////////////////////////////////////// 054//// QBEditor 055/** 056 * QBEditor is a top-level window containing the Query Builder UI. 057 */ 058public class QBEditor extends TableauFrame implements TableModelListener { 059 060 /** 061 * Construct an QB with the Schema and SQl. The SQL can be empty, but there 062 * needs to be a schema 063 * 064 * @param factory 065 * a Tableau Factory 066 * @param sqlAttr 067 * a SQL XML String document 068 * @param schemaAttr 069 * a Schema XML String document 070 * @param title 071 * the title 072 */ 073 public QBEditor(QBTableauFactory factory, StringAttribute sqlAttr, 074 StringAttribute schemaAttr, String title) { 075 // NOTE: Create with no status bar, since we have no use for it now. 076 super(null, null); 077 078 _factory = factory; 079 _sqlAttr = sqlAttr; 080 _schemaAttr = schemaAttr; 081 _title = title; 082 083 String sqlStr = _sqlAttr.getExpression(); 084 String schemaStr = _schemaAttr.getExpression(); 085 086 JComponent compToAdd = null; 087 088 boolean parsedOK = false; 089 if (schemaStr != null && schemaStr.length() > 0) { 090 DSSchemaIFace schemaIFace = DBSchemaParserEmitter 091 .parseSchemaDef(schemaStr); 092 if (schemaIFace != null) { 093 DBQueryDef queryDef = null; 094 if (sqlStr != null && sqlStr.length() > 0) { 095 queryDef = DBQueryDefParserEmitter.parseQueryDef( 096 schemaIFace, sqlStr); 097 } 098 099 _qbApp = new QBApp(this); 100 _qbApp.setExternalTMListener(this); 101 102 _qbApp.set(schemaIFace, queryDef); 103 compToAdd = _qbApp; 104 parsedOK = true; 105 } 106 } 107 108 if (!parsedOK) { 109 JLabel label = new JLabel( 110 "\nThere was a problem loading the schema or it was empty.\n\n"); 111 label.setHorizontalAlignment(JLabel.CENTER); 112 label.setForeground(Color.RED); 113 compToAdd = label; 114 } 115 116 getContentPane().add(compToAdd, BorderLayout.CENTER); 117 _initialSaveAsFileName = "qb.query"; 118 119 super.hideMenuBar(); 120 } 121 122 public void pack() { 123 // this sets off the Top.pack() method, which kicks off a thread... 124 super.pack(); 125 126 // ...so we need to start our own thread to wait for it to finish 127 deferIfNecessary(new WaitThread(this, _title)); 128 } 129 130 // ///////////////////////////////////////////////////////////////// 131 // // public variables //// 132 133 // ///////////////////////////////////////////////////////////////// 134 // // public methods //// 135 136 /** 137 * React to notification that an attribute or set of attributes changed. 138 */ 139 /* 140 * public void changedUpdate(DocumentEvent e) { // Do nothing... We don't 141 * care about attributes. } 142 */ 143 144 // ----------------------------------------------------------------------------- 145 // -- TableModelListener Interface 146 // ----------------------------------------------------------------------------- 147 public void tableChanged(TableModelEvent e) { 148 setModified(true); 149 } 150 151 /** 152 * For cancel button 153 * 154 * */ 155 public boolean closeWindows() { 156 _needPopDialog = false; // don't need pop dialog 157 _fromCancel = true; 158 boolean close = _close(); 159 _needPopDialog = true; // reset it 160 _fromCancel = false; // reset it 161 return close; 162 } 163 164 /** 165 * For okay button 166 * 167 * */ 168 public boolean save() { 169 _needPopDialog = false; // don't need pop dialog 170 171 if (isModified()) { 172 _save(); 173 } 174 boolean close = _close(); 175 _needPopDialog = true; // reset it 176 return close; 177 } 178 179 // ///////////////////////////////////////////////////////////////// 180 // // protected methods //// 181 182 /** 183 * Clear the current contents. First, check to see whether the contents have 184 * been modified, and if so, then prompt the user to save them. A return 185 * value of false indicates that the user has canceled the action. 186 * 187 * @return False if the user cancels the clear. 188 */ 189 protected boolean _clear() { 190 if (super._clear()) { 191 // text.setText(""); 192 return true; 193 } else { 194 return false; 195 } 196 } 197 198 /** 199 * Display more detailed information than given by _about(). 200 */ 201 protected void _help() { 202 // FIXME: Give instructions for the editor here. 203 _about(); 204 } 205 206 /** 207 * Print the contents. 208 */ 209 protected void _print() { 210 // FIXME: What should we print? 211 super._print(); 212 } 213 214 /** 215 * Override to query whether to apply the changes, if any. 216 * 217 * @return False if the user cancels on a apply query. 218 */ 219 protected boolean _close() { 220 // NOTE: The superclass doesn't do the right thing here, 221 // since it requires an associated Tableau. 222 223 // NOTE: We use dispose() here rather than just hiding the 224 // window. This ensures that derived classes can react to 225 // windowClosed events rather than overriding the 226 // windowClosing behavior given here. 227 boolean returnValue = true; 228 if (isModified() && _needPopDialog) { 229 230 if (_queryForApply()) { 231 dispose(); 232 } else { 233 return false; 234 } 235 } else { 236 // Window is not modified, so just dispose. 237 if (_fromCancel || !isModified()) { 238 setModified(false); 239 } 240 dispose(); 241 } 242 243 // Ensure that a new editor is opened next time. 244 this._factory.clear(); 245 246 return returnValue; 247 } 248 249 /** 250 * Override the base class to apply the change to the attribute. 251 * 252 * @return True if the save succeeded. 253 */ 254 protected boolean _save() { 255 // Issue a change request to ensure the change is 256 // applied at a safe time and that the model is marked 257 // modified. 258 DBQueryDef queryDef = new DBQueryDef(); 259 _qbApp.fillQueryDef(queryDef); 260 NamedObj context = (NamedObj) _sqlAttr.getContainer(); 261 String request = "<property name=\"" 262 + _sqlAttr.getName() 263 + "\" value=\"" 264 // + StringUtilities.escapeForXML(_factory.getText()) 265 + StringUtilities.escapeForXML(DBQueryDefParserEmitter 266 .emitXML(queryDef)) + "\"/>"; 267 // System.out.println("request: "+request+"\n\n"); 268 context.requestChange(new MoMLChangeRequest(this, context, request)); 269 setModified(true); 270 return true; 271 } 272 273 // ///////////////////////////////////////////////////////////////// 274 // // private methods //// 275 276 // Open a dialog to prompt the user to apply the data. 277 // Return false if the user clicks "cancel", and otherwise return true. 278 private boolean _queryForApply() { 279 Object[] options = { "Apply", "Discard changes", "Cancel" }; 280 String query = "Apply changes to " + _sqlAttr.getFullName() + "?"; 281 // Show the MODAL dialog 282 int selected = JOptionPane.showOptionDialog(this, query, 283 "Apply Changes?", JOptionPane.YES_NO_CANCEL_OPTION, 284 JOptionPane.QUESTION_MESSAGE, null, options, options[0]); 285 if (selected == 0) { 286 return _save(); 287 } else if (selected == 1) { 288 setModified(false); 289 return true; 290 } 291 return false; 292 } 293 294 // ///////////////////////////////////////////////////////////////// 295 // // protected variables //// 296 297 /** The scroll pane containing the text area. */ 298 protected JScrollPane _scrollPane; 299 protected QBApp _qbApp; 300 301 // ///////////////////////////////////////////////////////////////// 302 // // private members //// 303 304 private final QBTableauFactory _factory; 305 private StringAttribute _sqlAttr; 306 private StringAttribute _schemaAttr; 307 private String _title; 308 private boolean _needPopDialog = true; 309 private boolean _fromCancel = false; 310 311} 312 313class WaitThread extends Thread { 314 315 private QBEditor frame; 316 private String title; 317 318 public WaitThread(QBEditor frame, String title) { 319 this.frame = frame; 320 this.title = title; 321 this.setPriority(this.getPriority() - 2); 322 } 323 324 public void run() { 325 326 int safetyCounter = 0; 327 328 while (!frame.isMenuPopulated()) { 329 this.yield(); 330 try { 331 this.sleep(5); 332 } catch (InterruptedException ex) { 333 } 334 if (safetyCounter++ > 200) 335 break; 336 } 337 frame.setTitle(title); 338 } 339}