package org.mobicents.slee.container.deployment.profile.jpa;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.slee.profile.query.And;
import javax.slee.profile.query.Equals;
import javax.slee.profile.query.GreaterThan;
import javax.slee.profile.query.GreaterThanOrEquals;
import javax.slee.profile.query.HasPrefix;
import javax.slee.profile.query.LessThan;
import javax.slee.profile.query.LessThanOrEquals;
import javax.slee.profile.query.LongestPrefixMatch;
import javax.slee.profile.query.Not;
import javax.slee.profile.query.NotEquals;
import javax.slee.profile.query.Or;
import javax.slee.profile.query.QueryExpression;
import javax.slee.profile.query.RangeMatch;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.component.ProfileSpecificationComponent;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MCompare;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MHasPrefix;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MLongestPrefixMatch;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MQuery;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MQueryExpression;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MQueryExpressionType;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MQueryOptions;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MQueryParameter;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.query.MRangeMatch;
/**
*
* JPAQueryBuilder.java
*
* @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
*/
public class JPAQueryBuilder {
private static Logger logger = Logger.getLogger(JPAQueryBuilder.class);
private ProfileSpecificationComponent psc;
public JPAQueryBuilder(ProfileSpecificationComponent psc)
{
this.psc = psc;
}
// ####################
// # STATIC QUERIES #
// ####################
private static HashMap<String, QueryWrapper> queriesMap = new HashMap<String, QueryWrapper>();
public void parseStaticQueries()
{
List<MQuery> queries = this.psc.getDescriptor().getQueryElements();
for(MQuery query : queries)
{
MQueryOptions queryOptions = query.getQueryOptions();
boolean queryIsReadOnly = false;
long queryMaxMatches = -1;
if(queryOptions != null)
{
queryIsReadOnly = query.getQueryOptions().isReadOnly();
queryMaxMatches = query.getQueryOptions().getMaxMatches();
}
logger.info("Query :: Name[" + query.getName() + "], Options[" + (queryOptions == null ? "" : ("Read-Only[" + queryIsReadOnly + "], Max-Matches[" + queryMaxMatches + "]")) + "]" );
HashMap<String, Integer> queryParameters = new HashMap<String, Integer>();
int i = 0;
for(MQueryParameter parameter : query.getQueryParameters())
{
i++;
logger.info("Parameter :: Index[" + i + "], Name[" + parameter.getName() + "], Type[" + parameter.getType() + "]");
queryParameters.put( parameter.getName(), i );
}
long s = System.currentTimeMillis();
String sqlQuery = "SELECT " + (queryMaxMatches != -1 ? ("TOP " + queryMaxMatches) : "") + " * " + getExpression( query.getQueryExpression(), queryParameters, 0 );
logger.info("Query :: SQL[" + sqlQuery + "]");
long e = System.currentTimeMillis();
logger.info("Query :: Parsed in " + (e-s) + "ms.");
queriesMap.put( query.getName(), new QueryWrapper(sqlQuery, queryMaxMatches, queryIsReadOnly) );
}
}
private String getExpression(MQueryExpression expression, HashMap<String, Integer> queryParameters, int deepness)
{
return getExpression( expression, queryParameters, deepness, null );
}
private String getExpression(MQueryExpression expression, HashMap<String, Integer> queryParameters, int deepness, String sqlQuery)
{
List<MQueryExpression> expressions = new ArrayList<MQueryExpression>();
expressions.add(expression);
return getExpression( expressions, queryParameters, deepness, sqlQuery );
}
private String getExpression(List<MQueryExpression> expressions, HashMap<String, Integer> queryParameters, int deepness, String sqlQuery)
{
if(sqlQuery == null || sqlQuery.length() == 0)
sqlQuery = "FROM <CLASS> WHERE ";
String prefix = " ";
for(int i = 0; i < deepness; i++)
prefix += " ";
for(MQueryExpression expression : expressions)
{
switch(expression.getType())
{
case And:
logger.info( prefix + "+--> And" );
sqlQuery += " ( ";
sqlQuery = getExpression( expression.getAnd(), queryParameters, deepness+1, sqlQuery );
sqlQuery = replaceLast( sqlQuery, getLogicalOperator(expression.getType()), " ) ");
break;
case Compare:
MCompare compare = expression.getCompare();
logger.info( prefix + "+--> Compare[" + compare.getAttributeName() + " " + compare.getOp() + " " + getNotNull(queryParameters, compare.getAttributeName(), compare.getParameter(), compare.getValue()) + "]" );
sqlQuery += "C" + compare.getAttributeName() + getSQLOperator(compare.getOp()) + getNotNull(queryParameters, compare.getAttributeName(), compare.getParameter(), compare.getValue());
break;
case HasPrefix:
MHasPrefix hasPrefix = expression.getHasPrefix();
logger.info( prefix + "+--> HasPrefix[" + hasPrefix.getAttributeName() + " => " + getNotNull(queryParameters, hasPrefix.getAttributeName(), hasPrefix.getParameter(), hasPrefix.getValue()) + "]" );
sqlQuery += "C" + hasPrefix.getAttributeName() + " LIKE CONCAT(" + getNotNull(queryParameters, hasPrefix.getAttributeName(), hasPrefix.getParameter(), hasPrefix.getValue()) + ", '%') ";
break;
case LongestPrefixMatch:
MLongestPrefixMatch longestPrefixMatch = expression.getLongestPrefixMatch();
logger.info( prefix + "+--> LongestPrefixMatch[" + longestPrefixMatch.getAttributeName() + " >=< " + getNotNull(queryParameters, longestPrefixMatch.getAttributeName(), longestPrefixMatch.getParameter(), longestPrefixMatch.getValue()) + "]" );
sqlQuery += getNotNull(queryParameters, longestPrefixMatch.getAttributeName(), longestPrefixMatch.getParameter(), longestPrefixMatch.getValue()) + " LIKE CONCAT(" + "C" + longestPrefixMatch.getAttributeName() + ", '%')";
break;
case Not:
logger.info( prefix + "+--> Not" );
sqlQuery += " NOT ( ";
sqlQuery = getExpression( expression.getNot(), queryParameters, deepness+1, sqlQuery );
sqlQuery = replaceLast( sqlQuery, getLogicalOperator(expression.getType()), " ) ");
break;
case Or:
logger.info( prefix + "+--> Or" );
sqlQuery += " ( ";
sqlQuery = getExpression( expression.getOr(), queryParameters, deepness+1, sqlQuery );
sqlQuery = replaceLast( sqlQuery, getLogicalOperator(expression.getType()), " ) ");
break;
case RangeMatch:
MRangeMatch rangeMatch = expression.getRangeMatch();
logger.info( prefix + "+--> RangeMatch[" + rangeMatch.getAttributeName() + " => (" + getNotNull(queryParameters, rangeMatch.getAttributeName(), rangeMatch.getFromParameter(), rangeMatch.getFromValue()) + " : " + getNotNull(queryParameters, rangeMatch.getAttributeName(), rangeMatch.getToParameter(), rangeMatch.getToValue()) + ") ]" );
sqlQuery += "C" + rangeMatch.getAttributeName() + " >= " + getNotNull(queryParameters, rangeMatch.getAttributeName(), rangeMatch.getFromParameter(), rangeMatch.getFromValue()) + " AND " + "C" + rangeMatch.getAttributeName() + " <= " + getNotNull(queryParameters, rangeMatch.getAttributeName(), rangeMatch.getToParameter(), rangeMatch.getToValue());
break;
}
sqlQuery += getLogicalOperator(expression.getParentType());
}
return sqlQuery;
}
private String getNotNull(HashMap<String, Integer> queryParameters, String attributeName, String...strings)
{
for(String s : strings)
if( s != null)
{
if(queryParameters.containsKey(s))
{
return "?" + queryParameters.get(s);
}
else
{
Class retType = Integer.class;
try
{
retType = this.psc.getProfileCmpInterfaceClass().getMethod("get" + attributeName.replace(attributeName.charAt(0), Character.toUpperCase(attributeName.charAt(0)))).getReturnType();
}
catch ( Exception e ) {
e.printStackTrace();
}
if(retType == String.class || retType == Character.class || retType == char.class)
{
return "'" + s + "'";
}
return s;
}
}
return null;
}
private String getSQLOperator(String op)
{
// An op attribute.
// This attribute identifies the binary operator to apply to the Profile attribute
// value. It can be one of the following values: �equals�, �not-equals�, �less-than�,
// �less-than-or-equals�, �greater-than�, or �greater-than-or-equals�. If the Java
// type of the Profile attribute is boolean or java.lang.Boolean then only
// the �equals�, or �not-equals� operator are allowed.
String sqlOperator = null;
if(op.equals("equals"))
{
sqlOperator = " = ";
}
else if(op.equals("not-equals"))
{
sqlOperator = " != ";
}
else if(op.equals("less-than"))
{
sqlOperator = " < ";
}
else if(op.equals("less-than-or-equals"))
{
sqlOperator = " <= ";
}
else if(op.equals("greater-than"))
{
sqlOperator = " > ";
}
else if(op.equals("greater-than-or-equals"))
{
sqlOperator = " >= ";
}
return sqlOperator;
}
private String getLogicalOperator(MQueryExpressionType expType)
{
if (expType == null)
return "";
switch (expType)
{
case And:
return " AND ";
case Or:
return " OR ";
case Not:
return " NOT ";
default:
return "";
}
}
public static QueryWrapper getQuery(String queryName)
{
return queriesMap.get(queryName);
}
// #####################
// # DYNAMIC QUERIES #
// #####################
public static QueryWrapper parseDynamicQuery(QueryExpression query)
{
ArrayList<Object> params = new ArrayList<Object>();
long s = System.currentTimeMillis();
String sqlQuery = "SELECT * " + parseDynamicQuery( query, 0, null, params );
logger.info("Query :: SQL[" + sqlQuery + "]");
long e = System.currentTimeMillis();
logger.info("Query :: Parsed in " + (e-s) + "ms.");
return new QueryWrapper( sqlQuery, params );
}
private static String parseDynamicQuery(QueryExpression query, int deepness, String sqlQuery, ArrayList<Object> params)
{
if(sqlQuery == null || sqlQuery.length() == 0)
sqlQuery = "FROM <CLASS> WHERE ";
String prefix = " ";
for(int i = 0; i < deepness; i++)
prefix += " ";
if(query instanceof Equals)
{
Equals equals = (Equals)query;
logger.info( prefix + "+--> Equals[" + equals.getAttributeName() + " = " + equals.getAttributeValue() + "]" );
//sqlQuery += " C" + equals.getAttributeName() + " = " + escapeAttributeValue(equals.getAttributeValue()) + " ";
params.add( equals.getAttributeValue() );
sqlQuery += " C" + equals.getAttributeName() + " = ?" + params.size() + " ";
}
else if (query instanceof NotEquals)
{
NotEquals notEquals = (NotEquals)query;
logger.info( prefix + "+--> NotEquals[" + notEquals.getAttributeName() + " != " + notEquals.getAttributeValue() + "]" );
//sqlQuery += " C" + notEquals.getAttributeName() + " != " + escapeAttributeValue(notEquals.getAttributeValue()) + " ";
params.add( notEquals.getAttributeValue() );
sqlQuery += " C" + notEquals.getAttributeName() + " != ?" + params.size() + " ";
}
else if (query instanceof LessThan)
{
LessThan lessThan = (LessThan)query;
logger.info( prefix + "+--> LessThan[" + lessThan.getAttributeName() + " < " + lessThan.getAttributeValue() + "]" );
//sqlQuery += " C" + lessThan.getAttributeName() + " < " + escapeAttributeValue(lessThan.getAttributeValue()) + " ";
params.add( lessThan.getAttributeValue() );
sqlQuery += " C" + lessThan.getAttributeName() + " < ?" + params.size() + " ";
}
else if (query instanceof LessThanOrEquals)
{
LessThanOrEquals lessThanOrEquals = (LessThanOrEquals)query;
logger.info( prefix + "+--> LessThanOrEquals[" + lessThanOrEquals.getAttributeName() + " <= " + lessThanOrEquals.getAttributeValue() + "]" );
//sqlQuery += " C" + lessThanOrEquals.getAttributeName() + " <= " + escapeAttributeValue(lessThanOrEquals.getAttributeValue()) + " ";
params.add( lessThanOrEquals.getAttributeValue() );
sqlQuery += " C" + lessThanOrEquals.getAttributeName() + " <= ?" + params.size() + " ";
}
else if (query instanceof GreaterThan)
{
GreaterThan greaterThan = (GreaterThan)query;
logger.info( prefix + "+--> GreaterThan[" + greaterThan.getAttributeName() + " > " + greaterThan.getAttributeValue() + "]" );
//sqlQuery += " C" + greaterThan.getAttributeName() + " > " + escapeAttributeValue(greaterThan.getAttributeValue()) + " ";
params.add( greaterThan.getAttributeValue() );
sqlQuery += " C" + greaterThan.getAttributeName() + " > ?" + params.size() + " ";
}
else if (query instanceof GreaterThanOrEquals)
{
GreaterThanOrEquals greaterThanOrEquals = (GreaterThanOrEquals)query;
logger.info( prefix + "+--> GreaterThanOrEquals[" + greaterThanOrEquals.getAttributeName() + " >= " + greaterThanOrEquals.getAttributeValue() + "]" );
//sqlQuery += " C" + greaterThanOrEquals.getAttributeName() + " >= " + escapeAttributeValue(greaterThanOrEquals.getAttributeValue()) + " ";
params.add( greaterThanOrEquals.getAttributeValue() );
sqlQuery += " C" + greaterThanOrEquals.getAttributeName() + " >= ?" + params.size() + " ";
}
else if (query instanceof And)
{
logger.info( prefix + "+--> And" );
And and = (And)query;
sqlQuery += "(";
for(QueryExpression subAnd : and.getExpressions())
{
sqlQuery = parseDynamicQuery( subAnd, deepness+1, sqlQuery, params );
sqlQuery += " AND ";
}
sqlQuery = replaceLast(sqlQuery, " AND ", ")");
}
else if (query instanceof Or)
{
logger.info( prefix + "+--> Or" );
Or or = (Or)query;
sqlQuery += "(";
for(QueryExpression subOr : or.getExpressions())
{
sqlQuery = parseDynamicQuery( subOr, deepness+1, sqlQuery, params );
sqlQuery += " OR ";
}
sqlQuery = replaceLast(sqlQuery, " OR ", ")");
}
else if (query instanceof Not)
{
logger.info( prefix + "+--> Not" );
Not not = (Not)query;
sqlQuery += " NOT ( ";
sqlQuery = parseDynamicQuery( not.getExpression(), deepness+1, sqlQuery, params );
sqlQuery += ")";
}
else if (query instanceof LongestPrefixMatch)
{
LongestPrefixMatch longestPrefixMatch = (LongestPrefixMatch)query;
logger.info( prefix + "+--> LongestPrefixMatch[" + longestPrefixMatch.getAttributeName() + " >=< " + longestPrefixMatch.getAttributeValue() + "]" );
//sqlQuery += " " + escapeAttributeValue(longestPrefixMatch.getAttributeValue()) + " LIKE CONCAT(" + "C" + longestPrefixMatch.getAttributeName() + ", '%') ";
params.add( longestPrefixMatch.getAttributeValue() );
sqlQuery += " ?" + params.size() + " LIKE CONCAT(" + "C" + longestPrefixMatch.getAttributeName() + ", '%') ";
}
else if (query instanceof HasPrefix)
{
HasPrefix hasPrefix = (HasPrefix)query;
logger.info( prefix + "+--> HasPrefix[" + hasPrefix.getAttributeName() + " << " + hasPrefix.getAttributeValue() + "]" );
//sqlQuery += " C" + hasPrefix.getAttributeName() + " LIKE CONCAT(" + escapeAttributeValue(hasPrefix.getAttributeValue()) + ", '%') ";
params.add( hasPrefix.getAttributeValue() );
sqlQuery += " C" + hasPrefix.getAttributeName() + " LIKE CONCAT(" + "?" + params.size() + ", '%') ";
}
else if (query instanceof RangeMatch)
{
RangeMatch rangeMatch = (RangeMatch)query;
logger.info( prefix + "+--> RangeMatch[" + rangeMatch.getAttributeName() + " > " + rangeMatch.getFromValue() + " && " + rangeMatch.getAttributeName() + " < " + rangeMatch.getToValue() + "]" );
params.add( rangeMatch.getFromValue() );
params.add( rangeMatch.getToValue() );
sqlQuery += " C" + rangeMatch.getAttributeName() + " >= " + "?" + (params.size()-1) + " AND " + "C" + rangeMatch.getAttributeName() + " <= " + "?" + params.size() + " ";
}
return sqlQuery;
}
// ######################
// # COMMON FOR QUERIES #
// ######################
private static String replaceLast(String sourceString, String toBeReplaced, String replacement)
{
StringBuilder x = new StringBuilder(sourceString);
int liof = x.lastIndexOf(toBeReplaced);
if(liof >= 0)
x.replace(liof, liof+4, replacement);
return new String(x);
}
}