001    /* JAPI - (Yet another (hopefully) useful) Java API
002     *
003     * Copyright (C) 2004-2006 Christian Hujer
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License as
007     * published by the Free Software Foundation; either version 2 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful, but
011     * WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     * General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018     * 02111-1307, USA.
019     */
020    
021    package net.sf.japi.sql;
022    
023    import java.sql.SQLException;
024    import java.sql.DatabaseMetaData;
025    import java.sql.ResultSet;
026    import java.util.ArrayList;
027    import java.util.List;
028    import javax.swing.event.EventListenerList;
029    import javax.swing.event.TreeModelListener;
030    import javax.swing.event.TreeModelEvent;
031    import javax.swing.tree.TreePath;
032    import javax.swing.tree.TreeModel;
033    
034    /** A TreeModel displaying the catalogs of a database (usually tables and views) as tree.
035     * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
036     */
037    @SuppressWarnings({"ObjectEquality"})
038    public class DatabaseTreeModel implements TreeModel {
039    
040        /** The event listeners. */
041        private EventListenerList listenerList = new EventListenerList();
042    
043        /** The Database nodes in the tree. */
044        private List<CatalogTreeNode> catalogs = new ArrayList<CatalogTreeNode>();
045    
046        /** The Database Metadata of the database this treemodel reflects. */
047        private DatabaseMetaData databaseMetaData;
048    
049        /** Create a DatabaseTreeModel.
050         * Thw tree model is not yet connected to databaseMetaData.
051         */
052        public DatabaseTreeModel() {
053        }
054    
055        /** Create a DatabaseTreeModel for a Database.
056         * @param databaseMetaData Database Metadata for this tree model
057         * @throws SQLException in case of database problems
058         */
059        public DatabaseTreeModel(final DatabaseMetaData databaseMetaData) throws SQLException {
060            this.databaseMetaData = databaseMetaData;
061            refresh();
062        }
063    
064        /** Refresh the data of this model from the database.
065         * @throws SQLException in case of database problems
066         */
067        public void refresh() throws SQLException {
068            catalogs.clear();
069            if (databaseMetaData != null) {
070                ResultSet rs = null;
071                try {
072                    rs = databaseMetaData.getCatalogs();
073                    while (rs.next()) {
074                        try {
075                            catalogs.add(new CatalogTreeNode(rs.getString(1)));
076                        } catch (final SQLException e) {
077                            System.err.println(e);
078                            // TODO
079                        }
080                    }
081                } finally {
082                    try { rs.close(); } catch (final Exception e) { /* ignore **/ } finally { rs = null; }
083                }
084            }
085            fireTreeStructureChanged();
086        }
087    
088        /** Set the databaseMetaData for this model.
089         * @param databaseMetaData database meta data for this model
090         * @throws SQLException in case of database problems
091         */
092        public void setDatabaseMetaData(final DatabaseMetaData databaseMetaData) throws SQLException {
093            this.databaseMetaData = databaseMetaData;
094            refresh();
095        }
096    
097        /** Get the databaseMetaData of this model.
098         * @return databaseMetaData of this model
099         */
100        public DatabaseMetaData getDatabaseMetaData() {
101            return databaseMetaData;
102        }
103    
104        /** {@inheritDoc} */
105        public void addTreeModelListener(final TreeModelListener l) {
106            listenerList.add(TreeModelListener.class, l);
107        }
108    
109        /** {@inheritDoc} */
110        public Object getChild(final Object parent, final int index) {
111            if (parent == this) {
112                return catalogs.get(index);
113            } else {
114                assert parent instanceof CatalogTreeNode;
115                return ((CatalogTreeNode) parent).getTable(index);
116            }
117        }
118    
119        /** {@inheritDoc} */
120        public int getChildCount(final Object parent) {
121            if (parent == this) {
122                return catalogs.size();
123            } else {
124                return ((CatalogTreeNode) parent).getTableCount();
125            }
126        }
127    
128        /** {@inheritDoc} */
129        public int getIndexOfChild(final Object parent, final Object child) {
130            if (parent == this) {
131                return catalogs.indexOf(child);
132            } else {
133                return ((CatalogTreeNode) parent).getTableIndex((CatalogTreeNode.TableTreeNode) child);
134            }
135        }
136    
137        /** {@inheritDoc} */
138        public Object getRoot() {
139            return this;
140        }
141    
142        /** {@inheritDoc} */
143        public boolean isLeaf(final Object node) {
144            return node instanceof CatalogTreeNode.TableTreeNode;
145        }
146    
147        /** {@inheritDoc} */
148        public void removeTreeModelListener(final TreeModelListener l) {
149            listenerList.remove(TreeModelListener.class, l);
150        }
151    
152        /** {@inheritDoc} */
153        public void valueForPathChanged(final TreePath path, final Object newValue) {
154        }
155    
156        /** Event fire if the tree has changed. */
157        private void fireTreeStructureChanged() {
158            TreeModelEvent e = null;
159            final Object[] listeners = listenerList.getListenerList();
160            for (int i = listeners.length - 2; i >= 0; i -= 2) {
161                if (listeners[i] == TreeModelListener.class) {
162                    if (e == null) {
163                        e = new TreeModelEvent(this, new Object[] { this });
164                    }
165                    ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e);
166                }
167            }
168        }
169    
170        /** A TreeNode reflecting a Database. */
171        public class CatalogTreeNode {
172    
173            /** The name of this catalog. */
174            private String catalog;
175    
176            /** The tables of this catalog. */
177            private List<TableTreeNode> tables = new ArrayList<TableTreeNode>();
178    
179            /** Create a CatalogTreeNode.
180             * @throws SQLException in case of database problems
181             */
182            CatalogTreeNode(final String catalog) throws SQLException {
183                this.catalog = catalog;
184                ResultSet rs = null;
185                try {
186                    rs = databaseMetaData.getTables(catalog, null, null, null);
187                    while (rs.next()) {
188                        tables.add(new TableTreeNode(rs.getString("TABLE_NAME")));
189                    }
190                } finally {
191                    try { rs.close(); } catch (final Exception e) { /* ignore **/ } finally { rs = null; }
192                }
193            }
194    
195            /** Get a table with a certain index.
196             * @param index index of table to get
197             * @return table for <var>index</var>
198             */
199            TableTreeNode getTable(final int index) {
200                return tables.get(index);
201            }
202    
203            /** Get the number of tables.
204             * @return number of tables
205             */
206            public int getTableCount() {
207                return tables.size();
208            }
209    
210            /** Get the index of a table.
211             * @param table Table to get index of
212             * @return index of <var>table</var>
213             */
214            public int getTableIndex(final TableTreeNode table) {
215                return tables.indexOf(table);
216            }
217    
218            /** {@inheritDoc} */
219            @Override public String toString() {
220                return catalog;
221            }
222    
223            /** A TreeNode reflecting a Table. */
224            public class TableTreeNode {
225    
226                /** The name of this table. */
227                private String table;
228    
229                /** Create a TableTreeNode. */
230                TableTreeNode(final String table) {
231                    this.table = table;
232                }
233    
234                /** Get the name of the table this TableTreeNode represents.
235                 * @return table name
236                 */
237                public String getTableName() {
238                    return catalog + '.' + table;
239                }
240    
241                /** Get the name of the catalog this TableTreeNode is in.
242                 * @return catalog name
243                 */
244                public String getCatalogName() {
245                    return catalog;
246                }
247    
248                /** {@inheritDoc} */
249                @Override public String toString() {
250                    return table;
251                }
252    
253            } // class TableTreeNode
254    
255        } // class CatalogTreeNode
256    
257    } // class DatabaseTreeModel