001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2002-2005 Julian Hyde
008// Copyright (C) 2005-2009 Pentaho and others
009// All Rights Reserved.
010*/
011package mondrian.gui;
012
013import org.apache.log4j.Logger;
014
015import java.sql.*;
016import java.util.*;
017import javax.swing.event.TreeModelListener;
018import javax.swing.tree.TreePath;
019
020/**
021 *
022 * @author  sean
023 */
024public class JdbcTreeModel implements javax.swing.tree.TreeModel {
025
026    private static final Logger LOGGER = Logger.getLogger(JdbcTreeModel.class);
027
028    private Vector treeModelListeners = new Vector();
029    Connection connection;
030    DatabaseMetaData metadata;
031    List catalogs;
032    Node root;
033
034    /** Creates a new instance of JDBCTreeModel */
035    public JdbcTreeModel(Connection c) {
036        connection = c;
037        try {
038            metadata = connection.getMetaData();
039            catalogs = new ArrayList();
040            String catalogName = connection.getCatalog();
041            Node cat = new Node(catalogName, Node.CATALOG);
042
043            ResultSet trs = metadata.getTables(cat.name, null, null, null);
044            try {
045                while (trs.next()) {
046                    // Oracle 10g Driver returns bogus BIN$ tables that cause
047                    // exceptions
048                    String tbname = trs.getString("TABLE_NAME");
049                    if (!tbname.matches("(?!BIN\\$).+")) {
050                        continue;
051                    }
052
053                    Node table = new Node(trs.getString(3), Node.TABLE);
054                    cat.children.add(table);
055                    //get the tables for each catalog.
056                    ResultSet crs =
057                        metadata.getColumns(cat.name, null, table.name, null);
058                    try {
059                        while (crs.next()) {
060                            Node column =
061                                new Node(crs.getString(4), Node.COLUMN);
062                            table.children.add(column);
063                        }
064                    } finally {
065                        try {
066                            if (crs != null) {
067                                crs.close();
068                            }
069                        } catch (Exception e) {
070                            // ignore
071                        }
072                    }
073                }
074            } finally {
075                try {
076                    if (trs != null) {
077                        trs.close();
078                    }
079                } catch (Exception e) {
080                    // ignore
081                }
082            }
083            root = cat;
084        } catch (Exception ex) {
085            LOGGER.error("JdbcTreeModel", ex);
086        }
087    }
088
089
090    /** Adds a listener for the <code>TreeModelEvent</code>
091     * posted after the tree changes.
092     *
093     * @param   l       the listener to add
094     * @see     #removeTreeModelListener
095     *
096     */
097    public void addTreeModelListener(TreeModelListener l) {
098        treeModelListeners.add(l);
099    }
100
101    /** Returns the child of <code>parent</code> at index <code>index</code>
102     * in the parent's
103     * child array.  <code>parent</code> must be a node previously obtained
104     * from this data source. This should not return <code>null</code>
105     * if <code>index</code>
106     * is a valid index for <code>parent</code> (that is <code>index >= 0 &&
107     * index < getChildCount(parent</code>)).
108     *
109     * @param   parent  a node in the tree, obtained from this data source
110     * @return  the child of <code>parent</code> at index <code>index</code>
111     *
112     */
113    public Object getChild(Object parent, int index) {
114        if (parent instanceof Node) {
115            return ((Node)parent).children.get(index);
116        }
117
118        return null;
119    }
120
121    /** Returns the number of children of <code>parent</code>.
122     * Returns 0 if the node
123     * is a leaf or if it has no children.  <code>parent</code> must be a node
124     * previously obtained from this data source.
125     *
126     * @param   parent  a node in the tree, obtained from this data source
127     * @return  the number of children of the node <code>parent</code>
128     *
129     */
130    public int getChildCount(Object parent) {
131        if (parent instanceof Node) {
132            return ((Node)parent).children.size();
133        }
134        return 0;
135    }
136
137    /** Returns the index of child in parent.  If <code>parent</code>
138     * is <code>null</code> or <code>child</code> is <code>null</code>,
139     * returns -1.
140     *
141     * @param parent a note in the tree, obtained from this data source
142     * @param child the node we are interested in
143     * @return the index of the child in the parent, or -1 if either
144     *    <code>child</code> or <code>parent</code> are <code>null</code>
145     *
146     */
147    public int getIndexOfChild(Object parent, Object child) {
148        if (parent instanceof Node) {
149            return ((Node)parent).children.indexOf(child);
150        }
151
152        return -1;
153    }
154
155    /** Returns the root of the tree.  Returns <code>null</code>
156     * only if the tree has no nodes.
157     *
158     * @return  the root of the tree
159     *
160     */
161    public Object getRoot() {
162        return root;
163    }
164
165    /** Returns <code>true</code> if <code>node</code> is a leaf.
166     * It is possible for this method to return <code>false</code>
167     * even if <code>node</code> has no children.
168     * A directory in a filesystem, for example,
169     * may contain no files; the node representing
170     * the directory is not a leaf, but it also has no children.
171     *
172     * @param   node  a node in the tree, obtained from this data source
173     * @return  true if <code>node</code> is a leaf
174     *
175     */
176    public boolean isLeaf(Object node) {
177        return getChildCount(node) == 0;
178    }
179
180    /** Removes a listener previously added with
181     * <code>addTreeModelListener</code>.
182     *
183     * @see     #addTreeModelListener
184     * @param   l       the listener to remove
185     *
186     */
187    public void removeTreeModelListener(TreeModelListener l) {
188        treeModelListeners.remove(l);
189    }
190
191    /** Messaged when the user has altered the value for the item identified
192     * by <code>path</code> to <code>newValue</code>.
193     * If <code>newValue</code> signifies a truly new value
194     * the model should post a <code>treeNodesChanged</code> event.
195     *
196     * @param path path to the node that the user has altered
197     * @param newValue the new value from the TreeCellEditor
198     *
199     */
200    public void valueForPathChanged(TreePath path, Object newValue) {
201    }
202
203    class Node {
204        static final int CATALOG = 0;
205        static final int TABLE = 1;
206        static final int COLUMN = 2;
207        String name;
208        int type;
209        ArrayList children;
210
211        public Node(String n, int t) {
212            name = n;
213            type = t;
214            children = new ArrayList();
215        }
216
217        public String toString() {
218            return name;
219        }
220    }
221}
222
223// End JdbcTreeModel.java