/*
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// You must accept the terms of that agreement to use this software.
//
// Copyright (C) 2002-2005 Julian Hyde
// Copyright (C) 2005-2009 Pentaho and others
// All Rights Reserved.
*/

package mondrian.gui;

import org.apache.log4j.Logger;

import java.sql.*;
import java.util.*;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreePath;

/**
 *
 * @author  sean
 */
public class JdbcTreeModel implements javax.swing.tree.TreeModel {

    private static final Logger LOGGER = Logger.getLogger(JdbcTreeModel.class);

    private Vector treeModelListeners = new Vector();
    Connection connection;
    DatabaseMetaData metadata;
    List catalogs;
    Node root;

    /** Creates a new instance of JDBCTreeModel */
    public JdbcTreeModel(Connection c) {
        connection = c;
        try {
            metadata = connection.getMetaData();
            catalogs = new ArrayList();
            String catalogName = connection.getCatalog();
            Node cat = new Node(catalogName, Node.CATALOG);

            ResultSet trs = metadata.getTables(cat.name, null, null, null);
            try {
                while (trs.next()) {
                    // Oracle 10g Driver returns bogus BIN$ tables that cause
                    // exceptions
                    String tbname = trs.getString("TABLE_NAME");
                    if (!tbname.matches("(?!BIN\\$).+")) {
                        continue;
                    }

                    Node table = new Node(trs.getString(3), Node.TABLE);
                    cat.children.add(table);
                    //get the tables for each catalog.
                    ResultSet crs =
                        metadata.getColumns(cat.name, null, table.name, null);
                    try {
                        while (crs.next()) {
                            Node column =
                                new Node(crs.getString(4), Node.COLUMN);
                            table.children.add(column);
                        }
                    } finally {
                        try {
                            if (crs != null) {
                                crs.close();
                            }
                        } catch (Exception e) {
                            // ignore
                        }
                    }
                }
            } finally {
                try {
                    if (trs != null) {
                        trs.close();
                    }
                } catch (Exception e) {
                    // ignore
                }
            }
            root = cat;
        } catch (Exception ex) {
            LOGGER.error("JdbcTreeModel", ex);
        }
    }


    /** Adds a listener for the <code>TreeModelEvent</code>
     * posted after the tree changes.
     *
     * @param   l       the listener to add
     * @see     #removeTreeModelListener
     *
     */
    public void addTreeModelListener(TreeModelListener l) {
        treeModelListeners.add(l);
    }

    /** Returns the child of <code>parent</code> at index <code>index</code>
     * in the parent's
     * child array.  <code>parent</code> must be a node previously obtained
     * from this data source. This should not return <code>null</code>
     * if <code>index</code>
     * is a valid index for <code>parent</code> (that is <code>index >= 0 &&
     * index < getChildCount(parent</code>)).
     *
     * @param   parent  a node in the tree, obtained from this data source
     * @return  the child of <code>parent</code> at index <code>index</code>
     *
     */
    public Object getChild(Object parent, int index) {
        if (parent instanceof Node) {
            return ((Node)parent).children.get(index);
        }

        return null;
    }

    /** Returns the number of children of <code>parent</code>.
     * Returns 0 if the node
     * is a leaf or if it has no children.  <code>parent</code> must be a node
     * previously obtained from this data source.
     *
     * @param   parent  a node in the tree, obtained from this data source
     * @return  the number of children of the node <code>parent</code>
     *
     */
    public int getChildCount(Object parent) {
        if (parent instanceof Node) {
            return ((Node)parent).children.size();
        }
        return 0;
    }

    /** Returns the index of child in parent.  If <code>parent</code>
     * is <code>null</code> or <code>child</code> is <code>null</code>,
     * returns -1.
     *
     * @param parent a note in the tree, obtained from this data source
     * @param child the node we are interested in
     * @return the index of the child in the parent, or -1 if either
     *    <code>child</code> or <code>parent</code> are <code>null</code>
     *
     */
    public int getIndexOfChild(Object parent, Object child) {
        if (parent instanceof Node) {
            return ((Node)parent).children.indexOf(child);
        }

        return -1;
    }

    /** Returns the root of the tree.  Returns <code>null</code>
     * only if the tree has no nodes.
     *
     * @return  the root of the tree
     *
     */
    public Object getRoot() {
        return root;
    }

    /** Returns <code>true</code> if <code>node</code> is a leaf.
     * It is possible for this method to return <code>false</code>
     * even if <code>node</code> has no children.
     * A directory in a filesystem, for example,
     * may contain no files; the node representing
     * the directory is not a leaf, but it also has no children.
     *
     * @param   node  a node in the tree, obtained from this data source
     * @return  true if <code>node</code> is a leaf
     *
     */
    public boolean isLeaf(Object node) {
        return getChildCount(node) == 0;
    }

    /** Removes a listener previously added with
     * <code>addTreeModelListener</code>.
     *
     * @see     #addTreeModelListener
     * @param   l       the listener to remove
     *
     */
    public void removeTreeModelListener(TreeModelListener l) {
        treeModelListeners.remove(l);
    }

    /** Messaged when the user has altered the value for the item identified
     * by <code>path</code> to <code>newValue</code>.
     * If <code>newValue</code> signifies a truly new value
     * the model should post a <code>treeNodesChanged</code> event.
     *
     * @param path path to the node that the user has altered
     * @param newValue the new value from the TreeCellEditor
     *
     */
    public void valueForPathChanged(TreePath path, Object newValue) {
    }

    class Node {
        static final int CATALOG = 0;
        static final int TABLE = 1;
        static final int COLUMN = 2;
        String name;
        int type;
        ArrayList children;

        public Node(String n, int t) {
            name = n;
            type = t;
            children = new ArrayList();
        }

        public String toString() {
            return name;
        }
    }
}

// End JdbcTreeModel.java
