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}