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.Color;
033import java.awt.Graphics;
034import java.awt.Polygon;
035import java.awt.Rectangle;
036import java.awt.event.MouseEvent;
037import java.util.Enumeration;
038import java.util.Vector;
039
040import javax.swing.SwingUtilities;
041
042import org.kepler.objectmanager.data.db.DSSchemaIFace;
043
044/**
045 * This class maintains all the joins for the query. It is a model, a view, and
046 * a controller.<br>
047 * Model - A vector of all the join items<br>
048 * View - It is responsible for drawing all the lines representing the joins
049 * Controller - When clicks occur it is asked for hit detection as to whether a
050 * link was clicked on.
051 */
052public class DBTableJoin {
053        private static final int LINE_LEN = 10;
054        private static final int TRI_SIZE = 3;
055
056        private int[] xArray = new int[5];
057        private int[] yArray = new int[5];
058
059        protected Vector mJoinItems = new Vector();
060        protected DSSchemaIFace mSchema = null;
061        protected DBTableDesktopPane mDesktopPane = null;
062        protected DBTableJoinItem mSelectedItem = null;
063
064        /**
065         * Constructor takes Schema object (interface)
066         * 
067         * @param aDeskTopPane
068         *            desktop pane that implements DSSchemaIFace
069         */
070        public DBTableJoin(DBTableDesktopPane aDeskTopPane) {
071                mDesktopPane = aDeskTopPane;
072                mSchema = (DSSchemaIFace) mDesktopPane;
073        }
074
075        /**
076         * Returns the Vector of Joins
077         * 
078         * @return vector
079         */
080        public Vector getJoins() {
081                return mJoinItems;
082        }
083
084        /**
085         * Clears all of the join items
086         * 
087         */
088        public void clear() {
089                for (Enumeration e = mJoinItems.elements(); e.hasMoreElements();) {
090                        DBTableJoinItem joinItem = (DBTableJoinItem) e.nextElement();
091                        joinItem.mItemLeft.setLinked(false);
092                        joinItem.mItemRight.setLinked(false);
093                        ;
094                }
095                mJoinItems.clear();
096        }
097
098        /**
099         * 
100         * @param aItem
101         * @param aTableId
102         * @param aTableName
103         *       */
104        private boolean isTableMatch(DBTableField aItem, int aTableId,
105                        String aTableName) {
106                return ((aTableId != -1 && aItem.getTable().getId() == aTableId) || (aTableId == -1 && aTableName
107                                .equals(aItem.getTable().getName())));
108        }
109
110        /**
111         * Removes a tables to and from joins. It looks up by id first and then
112         * table name
113         * 
114         * @param aTableId
115         *            the id of the table
116         * @param aTableName
117         *            the name of the table
118         */
119        public void removeTable(int aTableId, String aTableName) {
120                // System.out.println("Removing table: "+aTableId+"  "+aTableName);
121                // first collect all the join items to be removed
122                Vector joinsToRemove = new Vector();
123                for (Enumeration e = mJoinItems.elements(); e.hasMoreElements();) {
124                        DBTableJoinItem joinItem = (DBTableJoinItem) e.nextElement();
125                        if (isTableMatch(joinItem.mItemLeft, aTableId, aTableName)
126                                        || isTableMatch(joinItem.mItemRight, aTableId, aTableName)) {
127                                // System.out.println("Removing join: "+joinItem.mItemLeft.getTable().getId()+"  "+joinItem.mItemLeft.getTable().getName());
128                                // System.out.println("               "+joinItem.mItemRight.getTable().getId()+"  "+joinItem.mItemRight.getTable().getName());
129                                joinsToRemove.add(joinItem);
130                        }
131                }
132                // now remove all the join items we have collected
133                for (Enumeration e = joinsToRemove.elements(); e.hasMoreElements();) {
134                        mJoinItems.remove((DBTableJoinItem) e.nextElement());
135                }
136        }
137
138        /**
139         * Add a Join
140         * 
141         * @param aItem1
142         *            the left item
143         * @param aItem2
144         *            the right item
145         */
146        public void addJoin(DBTableField aItem1, DBTableField aItem2) {
147                if (aItem1 != null && aItem2 != null) {
148                        DBTableJoinItem item = new DBTableJoinItem(aItem1, aItem2);
149
150                        aItem1.setLinked(true);
151                        aItem2.setLinked(true);
152                        mJoinItems.add(item);
153                }
154        }
155
156        /**
157         * Add a Join by item
158         * 
159         * @param aLeft
160         *            the left part of the link
161         * @param aRight
162         *            the right part of the link
163         */
164        public void addJoin(DBSelectTableModelItem aLeft,
165                        DBSelectTableModelItem aRight) {
166                DBTableField field1 = mDesktopPane.getFieldById(aLeft);
167                DBTableField field2 = mDesktopPane.getFieldById(aRight);
168                if (field1 != null && field2 != null) {
169                        addJoin(field1, field2);
170                }
171        }
172
173        /**
174         * Helper class for debugging
175         * 
176         * @param aMsg
177         *            the text
178         * @param r
179         *            the rect
180         */
181        /*
182         * protected void printRect(String aMsg, Rectangle r){
183         * System.out.print(aMsg+" ["+r.x+","+r.y+","+r.width+","+r.height+"]"); }
184         */
185
186        /**
187         * Implements a paint method for painting all joins
188         * 
189         * @param g
190         *            the graphics object
191         */
192        public void paint(Graphics g) {
193                for (Enumeration e = mJoinItems.elements(); e.hasMoreElements();) {
194                        DBTableJoinItem joinItem = (DBTableJoinItem) e.nextElement();
195                        DBTableField item1 = joinItem.mItemLeft;
196                        DBTableField item2 = joinItem.mItemRight;
197
198                        DBTableFrame tableFrame1 = item1.getTable();
199                        DBTableFrame tableFrame2 = item2.getTable();
200
201                        int val1 = tableFrame1.getScrollValue();
202                        int val2 = tableFrame2.getScrollValue();
203
204                        Rectangle tfListRect1 = tableFrame1.getListBounds();
205                        Rectangle tfListRect2 = tableFrame2.getListBounds();
206                        Rectangle tfBnds1 = tableFrame1.getBounds();
207                        Rectangle tfBnds2 = tableFrame2.getBounds();
208
209                        Rectangle r1 = item1.getBounds();
210                        Rectangle r2 = item2.getBounds();
211                        // printRect("r1 ", r1);
212                        // System.out.println(" ");
213                        // printRect("r2 ", r2);
214                        // System.out.println(" ");
215
216                        r1.x = tfBnds1.x;
217                        r1.width = tfBnds1.width;
218                        int y = r1.y - val1;
219                        if (y < 0) {
220                                r1.setBounds(tfBnds1.x, tfBnds1.y + 5, tfBnds1.width, 1);
221                        } else if (y > tfListRect1.height) {
222                                r1.setBounds(tfBnds1.x, tfBnds1.y + tfBnds1.height - 1 - 5,
223                                                tfBnds1.width, 1);
224                        } else {
225                                r1.y = y + tfListRect1.y + tfBnds1.y;
226                        }
227
228                        r2.x = tfBnds2.x;
229                        r2.width = tfBnds2.width;
230
231                        y = r2.y - val2;
232                        if (y < 0) {
233                                r2.setBounds(tfBnds2.x, tfBnds2.y + 5, tfBnds2.width, 2);
234
235                        } else if (y > tfListRect2.height) {
236                                r2.setBounds(tfBnds2.x, tfBnds2.y + tfBnds2.height - 2 - 5,
237                                                tfBnds2.width, 2);
238
239                        } else {
240                                r2.y = y + tfListRect2.y + tfBnds2.y;
241                        }
242
243                        int r1HalfX = (int) r1.getCenterX();
244                        int r1HalfY = (int) r1.getCenterY();
245                        int r2HalfX = (int) r2.getCenterX();
246                        int r2HalfY = (int) r2.getCenterY();
247
248                        int r1Right = r1.x + r1.width;
249                        int r2Right = r2.x + r2.width;
250
251                        g.setColor(joinItem.isSelected() ? Color.yellow : Color.blue);
252
253                        if (r1.x > r2Right || r1.x >= r2.x && r1.x <= r2Right) {
254                                int xPntsRight[] = { r1.x, r1.x - TRI_SIZE, r1.x };
255                                int yPntsRight[] = { r1HalfY - TRI_SIZE, r1HalfY,
256                                                r1HalfY + TRI_SIZE };
257                                int xPntsLeft[] = { r2Right, r2Right + TRI_SIZE, r2Right };
258                                int yPntsLeft[] = { r2HalfY + TRI_SIZE, r2HalfY,
259                                                r2HalfY - TRI_SIZE };
260
261                                g.setColor(joinItem.isSelected() ? Color.yellow : Color.red);
262                                g.fillPolygon(xPntsRight, yPntsRight, 3);
263                                g.drawLine(r1.x, r1HalfY, r1.x - LINE_LEN, r1HalfY);
264                                g.drawLine(r1.x - LINE_LEN, r1HalfY, r2Right + LINE_LEN,
265                                                r2HalfY);
266                                g.drawLine(r2Right + LINE_LEN, r2HalfY, r2Right, r2HalfY);
267                                g.fillPolygon(xPntsLeft, yPntsLeft, 3);
268
269                                joinItem.changePolygon(0, r1.x, r1HalfY, r1.x - LINE_LEN,
270                                                r1HalfY);
271                                joinItem.changePolygon(1, r1.x - LINE_LEN, r1HalfY, r2Right
272                                                + LINE_LEN, r2HalfY);
273                                joinItem.changePolygon(2, r2Right + LINE_LEN, r2HalfY, r2Right,
274                                                r2HalfY);
275
276                        } else if (r2.x > r1Right || r2.x >= r1.x && r2.x <= r1Right) {
277                                int xPntsRight[] = { r2.x, r2.x - TRI_SIZE, r2.x };
278                                int yPntsRight[] = { r2HalfY - TRI_SIZE, r2HalfY,
279                                                r2HalfY + TRI_SIZE };
280                                int xPntsLeft[] = { r1Right, r1Right + TRI_SIZE, r1Right };
281                                int yPntsLeft[] = { r1HalfY + TRI_SIZE, r1HalfY,
282                                                r1HalfY - TRI_SIZE };
283
284                                g.fillPolygon(xPntsLeft, yPntsLeft, 3);
285                                g.drawLine(r2.x, r2HalfY, r2.x - LINE_LEN, r2HalfY);
286                                g.drawLine(r2.x - LINE_LEN, r2HalfY, r1Right + LINE_LEN,
287                                                r1HalfY);
288                                g.drawLine(r1Right + LINE_LEN, r1HalfY, r1Right, r1HalfY);
289                                g.fillPolygon(xPntsRight, yPntsRight, 3);
290
291                                joinItem.changePolygon(0, r2.x, r2HalfY, r2.x - LINE_LEN,
292                                                r2HalfY);
293                                joinItem.changePolygon(1, r2.x - LINE_LEN, r2HalfY, r1Right
294                                                + LINE_LEN, r1HalfY);
295                                joinItem.changePolygon(2, r1Right + LINE_LEN, r1HalfY, r1Right,
296                                                r1HalfY);
297                        }
298
299                        // debug only do not remove
300                        /*
301                         * Polygon[] p = joinItem.getPolygons(); g.setColor(Color.red);
302                         * g.drawPolygon(p[0]); g.setColor(Color.green);
303                         * g.drawPolygon(p[1]); g.setColor(Color.magenta);
304                         * g.drawPolygon(p[2]);
305                         */
306
307                }
308        }
309
310        /**
311         * Refreshes the DesktopPane
312         */
313        public void refresh() {
314                mDesktopPane.makeDirty();
315        }
316
317        /**
318         * Removes a link or "join" between two tables
319         * 
320         * @return true if link was deleted, false if not
321         */
322        public boolean removeSelectedLink() {
323                if (mSelectedItem != null) {
324                        mJoinItems.remove(mSelectedItem);
325                        mSelectedItem = null;
326                        refresh();
327                        return true;
328                }
329                return false;
330        }
331
332        /**
333         * Uses the mouse event to select or deselect a link
334         * 
335         * @param ev
336         *            mouse event
337         */
338        public void selectLink(MouseEvent ev) {
339                DBTableJoinItem oldSelectedItem = mSelectedItem;
340                DBTableJoinItem newSelectedItem = null;
341
342                for (Enumeration e = mJoinItems.elements(); e.hasMoreElements();) {
343                        DBTableJoinItem joinItem = (DBTableJoinItem) e.nextElement();
344                        Polygon[] polygons = joinItem.getPolygons();
345                        for (int i = 0; i < polygons.length; i++) {
346                                if (polygons[i].contains(ev.getPoint())) {
347                                        newSelectedItem = joinItem;
348                                        break;
349                                }
350                        }
351                }
352
353                if (newSelectedItem != null) {
354                        if (newSelectedItem == oldSelectedItem) {
355                                newSelectedItem.setIsSelected(!newSelectedItem.isSelected());
356                        } else {
357                                if (mSelectedItem != null) {
358                                        mSelectedItem.setIsSelected(false);
359                                }
360                                newSelectedItem.setIsSelected(true);
361
362                                mSelectedItem = newSelectedItem;
363                                // Make sure a list has focus so the delete key can be properly
364                                // processed
365                                SwingUtilities.invokeLater(new Runnable() {
366                                        public void run() {
367                                                mDesktopPane.getDesktopManager().activateFrame(
368                                                                mSelectedItem.getItemLeft().getTable());
369                                                mSelectedItem.getItemLeft().getTable().getList()
370                                                                .requestFocus();
371                                        }
372                                });
373                        }
374                } else {
375                        if (mSelectedItem != null) {
376                                mSelectedItem.setIsSelected(false);
377                                mSelectedItem = null;
378                        }
379                }
380                refresh();
381        }
382
383}