/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.core.utils.querybuilder;

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

import javax.persistence.EntityManager;
import javax.persistence.Query;

/**
 * The HibernateQueryFactory class contains static helper methods intended
 * to simplify creation of hibernate queries based on where clauses build
 * with querybuilder package.
 * @autor nico.mack@tudor.lu
 * @created 08/01/31
 */

//***************************************************************************
//* Class Definition and Members                                        	*
//***************************************************************************

public class HibernateQueryFactory 
	{
    
	private static Pattern
    
    c_PlaceHolderPattern = Pattern.compile ("(\\$WHERECLAUSE)",Pattern.CASE_INSENSITIVE);

//***************************************************************************
//* Class Primitives                                                        *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * sets the parameters for the specified query to the values defined in
 * conditions of specified where clause.
 * @param p_Query specifies the query to set parameters for.
 * @param p_Clause specifies the where clause to get values from.
 * @return teh specified query having its parameters initialized.
 */
//---------------------------------------------------------------------------

private static Query setParamaters (Query p_Query, WhereClause p_Clause)
	{
	Collection <Condition>	l_Conditions;
	Iterator <Condition>	l_ConditionIterator;
	Condition				l_Condition;
	HibernateCondition		l_HibernateCondition;
		
	l_Conditions = p_Clause.getAllConditions();
	l_ConditionIterator = l_Conditions.iterator();
	while (l_ConditionIterator.hasNext())
		{
		l_Condition = l_ConditionIterator.next();
		if (l_Condition instanceof HibernateCondition)
			{
			l_HibernateCondition = (HibernateCondition) l_Condition;
			if (l_HibernateCondition.specifiesParameter())
				{
				p_Query.setParameter(l_HibernateCondition.getParameter(), 
									 l_HibernateCondition.getValue());
				}
			}
		}
	
	return p_Query;
	}
	
//---------------------------------------------------------------------------
	
//***************************************************************************
//* Class Body                                                              *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * The buildQueryFromWhereClause creates and initializes a query using the
 * specified query string and the specified where clause.
 * @param p_QueryString specifies the first part of the query without the
 * where clause.
 * @param p_Clause specifies the where clause to be appended to the specified
 * query string to obtain the full query string.
 * @return A full initialized query object, i.e. with parameters already 
 * being set.
 */
//---------------------------------------------------------------------------
public static Query buildQueryFromWhereClause (EntityManager p_EntityManager, String p_QueryString, WhereClause p_Clause) throws Exception
{
	return buildQueryFromWhereClause(p_EntityManager, p_QueryString, p_Clause, null);
}


/**
 * The buildQueryFromWhereClause creates and initializes a query using the
 * specified query string and the specified where clause.
 * @param p_QueryString specifies the first part of the query without the
 * where clause.
 * @param p_Clause specifies the where clause to be appended to the specified
 * query string to obtain the full query string.
 * @return A full initialized query object, i.e. with parameters already 
 * being set.
 */
public static Query buildQueryFromWhereClause (EntityManager p_EntityManager, String p_QueryString, WhereClause p_Clause, String p_OrderBy) throws Exception
	{
	Query 					l_Query;
	StringBuilder			l_QueryString;
	
	if ((p_EntityManager == null) || (p_QueryString == null))  return null;
	
	if ((p_Clause == null) || (p_Clause.getNumberOfConditions() == 0))
		{
		l_Query = p_EntityManager.createQuery(p_QueryString);
		return l_Query;
		}
	
	l_QueryString	= new StringBuilder(p_QueryString);
		
	l_QueryString.append(" WHERE ").append(p_Clause.toString());
	
	if (p_OrderBy != null)
		l_QueryString.append(p_OrderBy);
	
	l_Query = p_EntityManager.createQuery(l_QueryString.toString());
	l_Query = setParamaters (l_Query,p_Clause);
	
	return l_Query;
	}

//---------------------------------------------------------------------------
/**
 * The buildQueryFromNestedWhereClause creates and initializes a query using the
 * specified query string and the specified where clause. The difference to
 * the buildQueryFromWhereClause method resides in the fact that this method
 * looks for the $WHERECLAUSE placeholder in the query string instead of simply
 * adding the where clause at the of the specified query string
 * @param p_QueryString specifies the static part of the query without the
 * where clause but with a placeholder (WHERECLAUSE) for it.
 * @param p_Clause specifies the where clause to be appended to the specified
 * query string to obtain the full query string.
 * @return A full initialized query object, i.e. with parameters already 
 * being set.
 */
//---------------------------------------------------------------------------

public static Query buildQueryFromNestedWhereClause (EntityManager p_EntityManager, String p_QueryString, WhereClause p_Clause) throws Exception
	{
	Query 					l_Query;	
	Matcher         		l_Matcher;
	StringBuffer    		l_Buffer;
	
	if (p_QueryString == null)  return null;
	
	l_Buffer = new StringBuffer ();
	l_Matcher = c_PlaceHolderPattern.matcher(p_QueryString);

	if ((p_Clause == null) || (p_Clause.getNumberOfConditions() == 0))
		{
		if (l_Matcher.find())
    		{
			l_Matcher.appendReplacement(l_Buffer, "");
			l_Matcher.appendTail(l_Buffer);
			p_QueryString = l_Buffer.toString();
    		}
			
		l_Query = p_EntityManager.createQuery(p_QueryString);
		return l_Query;
		}
               
	l_Buffer = new StringBuffer ();
	l_Matcher = c_PlaceHolderPattern.matcher(p_QueryString);

	if (l_Matcher.find())
    	{
        l_Matcher.appendReplacement (l_Buffer," WHERE " + p_Clause.toString());
        l_Matcher.appendTail(l_Buffer);
        p_QueryString = l_Buffer.toString();
    	}
    else p_QueryString += " WHERE " + p_Clause.toString();
   
    l_Query = p_EntityManager.createQuery(p_QueryString);
	l_Query = setParamaters (l_Query,p_Clause);
	
	return l_Query;
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* End of Class															*
//***************************************************************************
}
