package de.hsofttec.core5.datasource;

import java.util.Iterator;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tapestry.beaneditor.PropertyModel;
import org.apache.tapestry.grid.GridDataSource;
import org.apache.tapestry.ioc.internal.util.TapestryException;

import org.hibernate.QueryException;

import de.hsofttec.core5.dao.GenericDAO;

/**
 * grid datasource for hibernate queries.
 *
 * @author <a href="mailto:shomburg@hsofttec.com">shomburg</a>
 * @version $Id: HibernateDataSource.java 13 2007-09-27 16:25:32Z shomburg $
 */
public class HibernateDataSource implements GridDataSource
{
    private static Log _logger = LogFactory.getLog(HibernateDataSource.class);
    private GenericDAO _genericDAO;
    private String _hqlString;
    private String _hqlCountString;
    private int _rowCount = -1;
    private Iterator _pageRowList;

    public HibernateDataSource(GenericDAO genericDAO, String hqlString)
    {
        this(genericDAO, hqlString, null);
    }

    public HibernateDataSource(GenericDAO genericDAO, String hqlString, String hqlSpecialCountString)
    {
        _genericDAO = genericDAO;
        _hqlString = hqlString;
        _hqlCountString = hqlSpecialCountString;
    }

    /**
     * Returns the number of rows available in the data source.
     */
    public int getAvailableRows()
    {
        if (_rowCount < 0)
            _rowCount = getMaximumResultObjects();

        return _rowCount;
    }

    /**
     * Invoked to allow the source to prepare to present values. This gives the source a chance to
     * pre-fetch data (when appropriate) and informs the source of the desired sort order.
     *
     * @param startIndex the starting index to be retrieved
     * @param endIndex   the ending index to be retrieved
     * @param sortModel  the property model that defines what data will be used for sorting, or null if no
     *                   sorting is required (in which case, whatever natural order is provided by the
     *                   underlying data source will be used)
     * @param ascending  if true, then sort ascending, else decending
     */
    public void prepare(int startIndex, int endIndex, PropertyModel sortModel, boolean ascending)
    {
        if (_logger.isInfoEnabled())
            _logger.info(String.format("processing prepare(%d, %d) for '%s'", 
                                       startIndex, endIndex, _genericDAO.getPersistentClass().getName()));

        _pageRowList = _genericDAO.findByQuery(_hqlString, startIndex, endIndex + 1).iterator();
    }

    /**
     * Returns the row value at the provided index. This method will be invoked in sequential order.
     */
    public Object getRowValue(int index)
    {
        Object entityObject = null;

        if (_pageRowList.hasNext())
            entityObject = _pageRowList.next();

        return entityObject;
    }

    /**
     * Returns the type of value in the rows, or null if not known.
     *
     * @return the row type, or null
     */
    public Class getRowType()
    {
        return _genericDAO.getPersistentClass();
    }

    /**
     * get the maximum count of rows to display.
     */
    private int getMaximumResultObjects()
    {
        String tempQuery1;
        String queryString = _hqlString;

        try
        {
            if (_logger.isInfoEnabled())
                _logger.info(String.format("processing getMaximumResultObjects() for '%s'", 
                                           _genericDAO.getPersistentClass().getName()));

            if (_hqlCountString != null)
                tempQuery1 = _hqlCountString;
            else
                tempQuery1 = convertQueryString(queryString);

            Long result = (Long) _genericDAO.countByQuery(tempQuery1);
            if (result == null)
                return 0;

            return result.intValue();
        }
        catch (QueryException e)
        {
            throw new TapestryException("entity not \"mapped\" in hibernate.cfg.xml ?", this, e);
        }
    }

    /**
     * den SQL-Query so Aufbauen, das wir einen Count auf die Tabelle absetzen koennen.
     */
    private String convertQueryString(String originalQueryString)
    {
        String tempQueryString = StringUtils.substring(originalQueryString, 
                                                       StringUtils.indexOf(StringUtils.
                                                                           upperCase(originalQueryString), "FROM"));
        String convertedQueryString = "SELECT COUNT(*) ";

        // Split input with the pattern
        Pattern p = Pattern.compile("[\\s]+");
        String[] result = p.split(tempQueryString);

        for (String queryWord : result)
        {
            //
            // ist queryWord ein Query-Fragment, was in einem Count-Query nicht auftauchen darf ?
            //
            // if (queryWord.equalsIgnoreCase("LEFT") || 
                   queryWord.equalsIgnoreCase("JOIN") || 
                   queryWord.equalsIgnoreCase("FETCH"))
            if (queryWord.equalsIgnoreCase("FETCH"))
                continue;

            if (queryWord.equalsIgnoreCase("ORDER"))
                break;

            convertedQueryString += queryWord + " ";
        }

        if (_logger.isInfoEnabled())
            _logger.info("source: " + originalQueryString + System.getProperty("line.separator") +
                    "dest: " + convertedQueryString);

        return convertedQueryString;
    }
}
  • No labels