/*
 * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the
 * License at http://www.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.kernel.ui;


import com.arsdigita.bebop.PageState;

import java.util.Iterator;
import com.arsdigita.persistence.Session;
import com.arsdigita.persistence.DataQuery;
import com.arsdigita.persistence.SessionManager;
import java.math.BigDecimal;
import com.arsdigita.bebop.tree.TreeModel;
import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.bebop.tree.TreeNode;
import org.apache.log4j.Logger;


/**
 * 
 * A generic tree model for representing data base backed trees
 * in an efficient manner. The number of queries is proportional
 * to the number of branches in the tree, rather than the total
 * number of nodes.
 *
 * All that is required to use this class are two
 * custom data queries, the names of which will be
 * passed into the constructor. The first one is used
 * to pull out the name, id and number of children for the
 * root node. The following example shows an example
 * implementation for category tree, which should be customized
 * to suit your data model.
 *
 * <pre>
 * query getRootCategory {
 *     do {
 *        select g.name,
 *               g.category_id,
 *               count(sd.category_id) as sub_count
 *        from cat_categories g,
 *             cat_category_category_map sd
 *        where g.category_id = :objectID
 *          and sd.category_id(+) = g.category_id
 *        group by g.name, g.category_id
 *    } map {
 *        id = g.category_id;
 *        name = g.name;
 *        nchild = sub_count;
 *    }
 * }
 * </pre>
 *
 * The second data query does a similar task, but for
 * *all* children of a particular node. Again the following
 * example for categories can be customized by changing the
 * table names:
 *
 * <pre>
 * query getSubCategories {
 *     do {
 *         select g.name,
 *                g.category_id,
 *                count(sd2.category_id) as sub_count
 *         from cat_categories g,
 *              cat_category_category_map sd1,
 *              cat_category_category_map sd2
 *         where sd1.category_id = :objectID
 *           and g.category_id = sd1.related_category_id
 *           and sd2.category_id(+) = sd1.related_category_id
 *         group by g.name, g.category_id
 *     } map {
 *         id = g.category_id;
 *         name = g.name;
 *         nchild = sub_count;
 *     }
 * }
 * </pre>
 *
 *
 * @author Daniel Berrange 
 *
 * @version $Id: //core-platform/dev/src/com/arsdigita/kernel/ui/DataQueryTreeModel.java#11 $
 */
public class DataQueryTreeModel implements TreeModel {

    public static final String versionId = "$Id: //core-platform/dev/src/com/arsdigita/kernel/ui/DataQueryTreeModel.java#11 $ by $Author: dennis $, $DateTime: 2004/04/07 16:07:11 $";

    private static final Logger s_log =
        Logger.getLogger(DataQueryTreeModel.class);

    private BigDecimal m_root;
    private String m_getRootCategory;
    private String m_getSubCategories;

    public DataQueryTreeModel (String getRootCategory,
                               String getSubCategories) {
        this(null, getRootCategory, getSubCategories);
    }

    /**
     *  Constructor, which takes in the root Category.
     *
     * @param root the object id of the root category
     * @param getRootCategory the data query name for root category
     * @param getSubCategories the data query name for sub categories
     */
    public DataQueryTreeModel (BigDecimal root,
                               String getRootCategory,
                               String getSubCategories) {
        m_root = root;
        m_getRootCategory = getRootCategory;
        m_getSubCategories = getSubCategories;
    }


    /**
     * Obtains all the children of the node as an iterator,
     * returning CategoryTreeNodeLites.
     *
     *  @param n the TreeNode that is used to look for the children
     *  @param data the PageState to use for permissioning purposes
     *  @return an iterator of child TreeNodes.
     */
    public Iterator getChildren(TreeNode node, PageState data) {
        return getDataQueryTreeIterator
            ((DataQueryTreeNode)node, m_getSubCategories);
    }

    /**
     * Indicates whether the specified tree node has children.
     *
     *  @param n the TreeNode that is used to look for the children
     *  @param data the PageState to use for permissioning purposes
     *  @return an iterator of child TreeNodes.
     */
    public boolean hasChildren(TreeNode node, PageState state) {
        DataQueryTreeNode n = (DataQueryTreeNode)node;
        return n.hasChildren();
    }

    public TreeNode getRoot(PageState state) {
        Session session = SessionManager.getSession();
        DataQuery query = session.retrieveQuery(m_getRootCategory);

        if (m_root != null)
            query.setParameter("objectID", m_root);

        if (query.next()) {
            BigDecimal id = (BigDecimal)query.get("id");
            String name = (String)query.get("name");
            BigDecimal count = (BigDecimal)query.get("nchild");

            query.close();

            return new DataQueryTreeNode(id, name, count.intValue() > 0);
        }
        throw new UncheckedWrapperException("cannot find root node");
    }

    protected DataQueryTreeIterator getDataQueryTreeIterator
        (DataQueryTreeNode node, String getSubCategories) {
        return new DataQueryTreeIterator(node, getSubCategories);
    }
}
