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.ecoinformatics.seek.querybuilder;
031
032import java.awt.Component;
033import java.awt.Dimension;
034import java.awt.Graphics;
035import java.awt.Rectangle;
036import java.awt.event.KeyAdapter;
037import java.awt.event.KeyEvent;
038import java.awt.event.KeyListener;
039import java.awt.event.MouseAdapter;
040import java.awt.event.MouseEvent;
041import java.awt.event.MouseListener;
042import java.util.Enumeration;
043import java.util.Hashtable;
044import java.util.Vector;
045
046import javax.swing.JComponent;
047import javax.swing.JDesktopPane;
048import javax.swing.RepaintManager;
049import javax.swing.SwingUtilities;
050import javax.swing.event.InternalFrameEvent;
051import javax.swing.event.InternalFrameListener;
052import javax.swing.event.TableModelListener;
053
054import org.kepler.objectmanager.data.db.DSSchemaIFace;
055import org.kepler.objectmanager.data.db.DSTableIFace;
056
057/**
058 * Overrides JDesktopPane for laying out all the tables
059 */
060public class DBTableDesktopPane extends JDesktopPane implements DSSchemaIFace,
061                DBSelectTableFieldChangedListener, InternalFrameListener {
062
063        protected DBTableJoin mTableJoins = new DBTableJoin(this);
064        protected DSSchemaIFace mSchema = null;
065        protected Vector mTables = new Vector();
066        protected TableModelListener mTableModelListener = null;
067
068        private int mJifX = 0;
069
070        /**
071         * DBTableDesktopPane Constructor
072         */
073        public DBTableDesktopPane() {
074                setDoubleBuffered(true);
075
076                MouseListener mouseListener = new MouseAdapter() {
077                        public void mousePressed(MouseEvent ev) {
078                                mTableJoins.selectLink(ev);
079                        }
080                };
081                addMouseListener(mouseListener);
082        }
083
084        /**
085         * Finalize/Cleanup
086         */
087        public void finalize() {
088                mTableModelListener = null;
089        }
090
091        /**
092         * Sets a single listener of TableModel Changes
093         * 
094         * @param aL
095         *            the listener
096         */
097        public void setTableModelListener(TableModelListener aL) {
098                mTableModelListener = aL;
099        }
100
101        /**
102         * Returns the TableJoin Object
103         * 
104         * @return a joing object
105         */
106        public DBTableJoin getTableJoins() {
107                return mTableJoins;
108        }
109
110        /**
111         * Sets the Data Src Schema Object and creates all the tables for it
112         * 
113         * @param aSchemaDef
114         *            the schema
115         */
116        public void setSchema(DSSchemaIFace aSchemaDef) {
117                mSchema = aSchemaDef;
118                createSchemaTables(mSchema);
119        }
120
121        /**
122         * Returns the Schema Object
123         * 
124         * @return schema object
125         */
126        public DSSchemaIFace getSchema() {
127                return mSchema;
128        }
129
130        /**
131         * Return the appropriate DBTableField from the a desktoppane table frame by
132         * its Id (or table name if the id is -1, and then the field name
133         * 
134         * @param aItem
135         *            the item to have its field looked up
136         * @return the field in the table frame
137         */
138        public DBTableField getFieldById(DBSelectTableModelItem aItem) {
139                for (Enumeration et = mTables.elements(); et.hasMoreElements();) {
140                        DBTableFrame tblFrame = (DBTableFrame) et.nextElement();
141                        int id = aItem.getTableId();
142                        if ((id != -1 && tblFrame.getId() == id)
143                                        || (id == -1 && aItem.getTableName().equals(
144                                                        tblFrame.getName()))) {
145                                return (DBTableField) DBUIUtils.getFieldByName(tblFrame, aItem
146                                                .getName());
147                        }
148                }
149                return null;
150        }
151
152        /**
153         * Creates a table frame for each table in the schema
154         * 
155         * @param aSchemaDef
156         *            the schema
157         */
158        protected void createSchemaTables(DSSchemaIFace aSchemaDef) {
159                // Remove all current table frames here
160                clearTables();
161
162                if (mSchema != null) {
163                        // Create a table for each one in the schema
164                        int id = 0;
165                        for (Enumeration et = mSchema.getTables().elements(); et
166                                        .hasMoreElements();) {
167                                createTable((DSTableIFace) et.nextElement(), id++, -1, -1);
168                        }
169                }
170        }
171
172        /**
173         * Deletes and adds all the necessary frames
174         */
175        public void clearTables() {
176                // Remove all current table frames here
177                for (Enumeration et = mTables.elements(); et.hasMoreElements();) {
178                        this.remove((DBTableFrame) et.nextElement());
179                }
180                mTables.clear();
181                mJifX = 0;
182        }
183
184        /**
185         * Creates a single table frame from the table schema
186         * 
187         * @param aTableDef
188         *            the table schema
189         * @param aId
190         *            the unique id of the table
191         * @param aX
192         *            the x coord of the table's location in the builder
193         * @param aY
194         *            the y coord of the table's location in the builder
195         */
196        protected void createTable(DSTableIFace aTableDef, int aId, int aX, int aY) {
197                if (aTableDef == null)
198                        return;
199
200                if (aId == -1) {
201                        aId = this.getNewId();
202                }
203
204                // Create a table frame
205                int windowCount = this
206                                .getComponentCountInLayer(JDesktopPane.DEFAULT_LAYER.intValue());
207                int width = 100;
208
209                DBTableFrame jif = new DBTableFrame(aTableDef, aId);
210                jif.setClosable(true);
211                jif.addInternalFrameListener(this);
212                jif.setJoins(mTableJoins);
213
214                add(jif, JDesktopPane.DEFAULT_LAYER);
215
216                Dimension dim = jif.getPreferredSize();
217
218                // make sure when it is added that it is always visible
219                int panelWidth = getLayeredPaneAbove(jif).getBounds().width;
220                int panelHeight = getLayeredPaneAbove(jif).getBounds().height;
221                if (panelWidth > 0 && mJifX > panelWidth)
222                        mJifX = 0;
223
224                // this might not make it visible in the "y" direction
225                // but it will atleast be placed on the canvas
226                int yCoord = 20 * (windowCount % 10);
227                // if (panelHeight > 0 && (yCoord+dim.height) > panelHeight)
228                // yCoord = 0;
229
230                if (aX > -1 && aY > -1) {
231                        jif.setBounds(aX, aY, dim.width, dim.height);
232                } else {
233                        jif.setBounds(mJifX, yCoord, dim.width, dim.height);
234                }
235
236                mJifX += dim.width + 20;
237
238                // Set this internal frame to be selected
239
240                try {
241                        jif.setSelected(true);
242                } catch (java.beans.PropertyVetoException e2) {
243                }
244
245                jif.show();
246                mTables.add(jif);
247
248                KeyListener keyListener = new KeyAdapter() {
249                        public void keyPressed(KeyEvent e) {
250                                if (e.getKeyCode() == KeyEvent.VK_DELETE) {
251                                        if (mTableJoins.removeSelectedLink()) {
252                                                mTableModelListener.tableChanged(null);
253                                                e.consume();
254                                        }
255                                }
256                        }
257                };
258                jif.getList().addKeyListener(keyListener);
259                jif.setTableModelListener(mTableModelListener);
260        }
261
262        /**
263         * Returns a unique and unused id for a new table
264         * 
265         * @return a unique (unused) id
266         */
267        private int getNewId() {
268                Hashtable hash = new Hashtable();
269                for (Enumeration et = mTables.elements(); et.hasMoreElements();) {
270                        String idStr = Integer.toString(((DBTableFrame) et.nextElement())
271                                        .getId());
272                        hash.put(idStr, idStr);
273                }
274
275                for (int i = 0; i < Integer.MAX_VALUE; i++) {
276                        if (hash.get(Integer.toString(i)) == null) {
277                                return i;
278                        }
279                }
280                return -1;
281        }
282
283        /**
284         * Adds a new table by name to the workspace, this method looks up the table
285         * schema and adds it
286         * 
287         * @param aTableName
288         *            the tables name
289         */
290        public void addTableToWorkspace(String aTableName) {
291                createTable(DBUIUtils.getTableByName(mSchema, aTableName), getNewId(),
292                                -1, -1);
293                if (mTableModelListener != null)
294                        mTableModelListener.tableChanged(null);
295        }
296
297        /**
298         * Adds a new table by name to the workspace with a DBQueryDefTable object
299         * 
300         * @param aTable
301         *            the table object
302         */
303        public void addTableToWorkspace(DBQueryDefTable aTable) {
304                createTable(DBUIUtils.getTableByName(mSchema, aTable.getName()), aTable
305                                .getId(), aTable.getPnt().x, aTable.getPnt().y);
306                if (mTableModelListener != null)
307                        mTableModelListener.tableChanged(null);
308        }
309
310        /**
311         * Returns the preferred size of the desktop pane so it is large enough for
312         * all the tables (meaning we want to be able to scroll and see all the
313         * tables and not have them clipped)
314         * 
315         * @return the dimension
316         */
317        public Dimension getPreferredSize() {
318                int maxWidth = 0;
319                int maxHeight = 0;
320                Component[] components = getComponents();
321                for (int i = 0; i < components.length; i++) {
322                        Rectangle rect = components[i].getBounds();
323                        maxWidth = Math.max(rect.x + rect.width, maxWidth);
324                        maxHeight = Math.max(rect.y + rect.height, maxHeight);
325                }
326                return new Dimension(maxWidth, maxHeight);
327        }
328
329        /**
330         * Overrides paintChildren to make sure all the "joins" get painted
331         */
332        public void paintChildren(Graphics g) {
333                mTableJoins.paint(g);
334                super.paintChildren(g);
335        }
336
337        /**
338         * Overrides paint so it can "dirty" everything
339         */
340        public void repaint(long tm, int x, int y, int width, int height) {
341                Rectangle paintRect = new Rectangle(x, y, width, height);
342                Component[] components = getComponents();
343                for (int i = 0; i < components.length; i++) {
344                        Rectangle rect = components[i].getBounds();
345                        if (paintRect.intersects(rect)) {
346                                RepaintManager mgr = RepaintManager
347                                                .currentManager(components[i]);
348                                mgr.addDirtyRegion((JComponent) components[i], rect.x, rect.y,
349                                                rect.width, rect.height);
350                                /*
351                                 * Component[] children =
352                                 * ((Container)components[i]).getComponents(); for (int
353                                 * j=0;j<children.length;j++) { Rectangle childRect =
354                                 * children[j].getBounds(); RepaintManager childMgr =
355                                 * RepaintManager.currentManager(children[j]);
356                                 * mgr.addDirtyRegion((JComponent)children[j], childRect.x,
357                                 * childRect.y, childRect.width, childRect.height); }
358                                 */
359                        }
360                }
361
362                super.repaint(tm, x, y, width, height);
363
364        }
365
366        /**
367         * Makes sure the entire "panel" will be redrawn by marking the entire
368         * bounds "dirty"
369         */
370        protected void dirtyAll() {
371                Rectangle rect = getBounds();
372                RepaintManager mgr = RepaintManager.currentManager(this);
373                mgr.addDirtyRegion((JComponent) this, rect.x, rect.y, rect.width,
374                                rect.height);
375                SwingUtilities.invokeLater(new Runnable() {
376                        public void run() {
377                                repaint();
378                        }
379                });
380
381        }
382
383        /**
384         * Makes sure the entire "panel" will be redrawn
385         * 
386         */
387        public void makeDirty() {
388                SwingUtilities.invokeLater(new Runnable() {
389                        public void run() {
390                                dirtyAll();
391                        }
392                });
393        }
394
395        // --------------------------------------------------------------
396        // ------------ DSSchemaIFace -----------------
397        // --------------------------------------------------------------
398
399        /**
400         * Returns the Vector of TableFrame Objects
401         * 
402         * @return vector
403         */
404        public Vector getTables() {
405                return mTables;
406        }
407
408        /**
409         * Returns the name of the schema
410         * 
411         * @return string
412         */
413        public String getName() {
414                return mSchema != null ? mSchema.getName() : "";
415        }
416
417        // --------------------------------------------------------------
418        // ------- DBSelectTableFieldChangedListener Methods ------------
419        // --------------------------------------------------------------
420        /**
421         * Makes everything "dirty" and forces an update.
422         */
423        public void notifyFieldChanged() {
424                makeDirty();
425        }
426
427        // -----------------------------------------------
428        // ---------- InternalFrameListener --------------
429        // -----------------------------------------------
430        /**
431         * stubbed
432         */
433        public void internalFrameActivated(InternalFrameEvent e) {
434        }
435
436        /**
437         * Recieces notification that a table frame was closed. It removes all the
438         * joins to and from the table.
439         * 
440         * @param e
441         *            the event
442         */
443        public void internalFrameClosed(InternalFrameEvent e) {
444                // Remove it from our taable list
445                DBTableFrame tableFrame = (DBTableFrame) e.getInternalFrame();
446                mTables.remove(tableFrame);
447
448                // Remove any joins to and from the table
449                mTableJoins.removeTable(tableFrame.getId(), tableFrame.getName());
450                repaint();
451
452                if (mTableModelListener != null)
453                        mTableModelListener.tableChanged(null);
454        }
455
456        /**
457         * stubbed
458         */
459        public void internalFrameClosing(InternalFrameEvent e) {
460        }
461
462        /**
463         * stubbed
464         */
465        public void internalFrameDeactivated(InternalFrameEvent e) {
466        }
467
468        /**
469         * stubbed
470         */
471        public void internalFrameDeiconified(InternalFrameEvent e) {
472        }
473
474        /**
475         * stubbed
476         */
477        public void internalFrameIconified(InternalFrameEvent e) {
478        }
479
480        /**
481         * stubbed
482         */
483        public void internalFrameOpened(InternalFrameEvent e) {
484        }
485
486}