package io.robe.hibernate.criteria.hql;
import io.robe.common.dto.Increment;
import io.robe.common.utils.Validations;
import io.robe.hibernate.criteria.api.Criteria;
import io.robe.hibernate.criteria.api.CriteriaJoin;
import io.robe.hibernate.criteria.api.CriteriaParent;
import io.robe.hibernate.criteria.api.JoinRelation;
import io.robe.hibernate.criteria.api.cache.FieldMeta;
import io.robe.hibernate.criteria.api.criterion.Restriction;
import io.robe.hibernate.criteria.api.criterion.RestrictionList;
import io.robe.hibernate.criteria.api.projection.*;
import java.util.*;
/**
* Created by kamilbukum on 30/01/2017.
*/
public class HqlUtil {
/**
*
* @param criteria
* @param projection
* @param alias
* @param elements
* @param groupJoiner
* @return
*/
public static String selectForListByProjection(CriteriaParent criteria, Projection projection, String alias, TransformerUtil.Elements elements, StringJoiner groupJoiner){
String result;
if(projection instanceof IdentifierProjection) {
result = criteria.getAlias() + "." + criteria.getMeta().getIdentityName() + getAsKey(criteria, criteria.getMeta().getIdentityName(), alias);
} else if(projection instanceof ProjectionElements) {
ProjectionElements p = (ProjectionElements)projection;
String elementAlias = alias != null ? alias: p.getProperty();
if(elements.elementsMap == null) {
elements.elementsMap = new LinkedHashMap<>();
}
elements.elementsMap.put(p.getProperty(), elementAlias);
return null;
} else if(projection instanceof PropertyProjection) {
PropertyProjection p = (PropertyProjection)projection;
result = criteria.getAlias() + "." + p.getProperty() + getAsKey(criteria, p.getProperty(), alias);
if(p.isGrouped()) {
groupJoiner.add( criteria.getAlias() + "." + p.getProperty());
}
} else if(projection instanceof FunctionProjection) {
FunctionProjection pp = (FunctionProjection)projection;
if(FunctionProjection.Type.COUNT == pp.getFnType() && Validations.isEmptyOrNull(pp.getProperty())) {
return pp.getFnType().name() + "(1)" + getAsKey(criteria, "fn_" + pp.getFnType().name().toLowerCase(), alias);
} else {
return pp.getFnType().name() + "(" + criteria.getAlias() + "." + pp.getProperty() + ")" + getAsKey(criteria, "fn_" + pp.getProperty() + "_" + pp.getFnType().name().toLowerCase() , alias);
}
} else if(projection instanceof EnhancedProjection) {
EnhancedProjection p = (EnhancedProjection)projection;
result = selectForListByProjection(criteria, p.getProjection(), p.getAlias(),elements, groupJoiner);
} else if(projection instanceof ProjectionList){
ProjectionList p = (ProjectionList)projection;
StringJoiner joiner = new StringJoiner(", ");
for(int i = 0 ; i < p.getLength(); i++) {
Projection childProjection = p.getProjection(i);
String projectionString = selectForListByProjection(criteria, childProjection, null,elements, groupJoiner);
if(!Validations.isEmptyOrNull(projectionString)) {
joiner.add(projectionString);
}
}
result = joiner.toString();
} else {
throw new RuntimeException("Unknown Projection !"+ projection);
}
return result;
}
/**
*
* @param criteria
* @param selectAlias
* @param asAlias
* @return
*/
private static String getAsKey(CriteriaParent criteria, String selectAlias, String asAlias){
String alias;
if(asAlias != null) {
alias = asAlias.replaceAll("\\.", "_0_");
} else if(criteria.isRoot()) {
alias = selectAlias;
} else {
alias = criteria.getAlias() + "_0_" + selectAlias;
}
return " AS " + alias;
}
/**
*
* @param criteria
* @param restrictions
* @param restrictionJoiner
* @param qJoiner
* @param parameterMap
* @param restrictionOrder
*/
public static void generateRestrictions(
CriteriaParent criteria,
List<Restriction> restrictions,
StringJoiner restrictionJoiner,
StringJoiner qJoiner,
Map<String, Object> parameterMap,
Increment restrictionOrder){
for(Restriction restriction: restrictions) {
String result = null;
switch (restriction.getOperator()) {
case EQUALS:
result = restrictionToString(criteria, restriction, restrictionOrder.increment(), restriction.getValue(), "=", parameterMap);
break;
case IS_NULL:
result = criteria.getAlias() + "." + restriction.getName() + " IS NULL";
break;
case NOT_EQUALS:
result = criteria.getAlias() + "." + restrictionToString(criteria, restriction, restrictionOrder.increment(), restriction.getValue(), "!=", parameterMap);
break;
case IS_NOT_NULL:
result = restriction.getName() + " IS NOT NULL";
break;
case LESS_THAN:
result = restrictionToString(criteria, restriction, restrictionOrder.increment(), restriction.getValue(), " < ", parameterMap);
break;
case LESS_OR_EQUALS_THAN:
result = restrictionToString(criteria, restriction, restrictionOrder.increment(), restriction.getValue(), " <= ", parameterMap);
break;
case GREATER_THAN:
result = restrictionToString(criteria, restriction, restrictionOrder.increment(), restriction.getValue(), " > ", parameterMap);
break;
case GREATER_OR_EQUALS_THAN:
result = restrictionToString(criteria, restriction, restrictionOrder.increment(), restriction.getValue(), " >= ", parameterMap);
break;
case Q:
result = null;
qJoiner.add(criteria.getAlias() + "." + restriction.getName() + " LIKE " + ":" + restriction.getValueAlias());
parameterMap.putIfAbsent(restriction.getValueAlias(), getPercentValue(restriction));
break;
case CONTAINS:
Object containsValue = getPercentValue(restriction);
result = restrictionToString(criteria, restriction, restrictionOrder.increment(), containsValue, " LIKE ", parameterMap);
break;
case IN:
result = restrictionToString(
criteria,
restriction,
restrictionOrder.increment(),
restriction.getValue(),
" IN ", parameterMap
);
break;
case AND:
StringJoiner andJoiner = new StringJoiner(" AND ");
generateRestrictions(criteria, ((RestrictionList)restriction).getRestrictions(), andJoiner, qJoiner, parameterMap, restrictionOrder);
if(!"".equals(andJoiner.toString())) {
result = "( " + andJoiner.toString() + " )";
}break;
case OR:
StringJoiner orJoiner = new StringJoiner(" OR ");
generateRestrictions(criteria, ((RestrictionList)restriction).getRestrictions(), orJoiner, qJoiner, parameterMap, restrictionOrder);
if(!"".equals(orJoiner.toString())) {
result = "( " + orJoiner.toString() + " )";
}
}
if(result != null) {
restrictionJoiner.add(result);
}
}
}
/**
*
* @param criteria
* @param restriction
* @param operator
* @return
*/
private static String restrictionToString(CriteriaParent criteria, Restriction restriction, Integer order, Object value, String operator, Map<String, Object> parameterMap){
if(Validations.isEmptyOrNull(restriction.getValueAlias())) {
restriction.setValueAlias(criteria.getAlias() + "_" + restriction.getName()+ "_" + order);
}
parameterMap.put(restriction.getValueAlias(), value);
return criteria.getAlias() + "." + restriction.getName() + operator + (value instanceof Collection ? ( "(:" + restriction.getValueAlias() + ")"): ":" + restriction.getValueAlias());
}
private static Object getPercentValue(Restriction restriction) {
if(restriction.getValue() != null) {
return "%" + restriction.getValue() + "%";
}
return "%%";
}
/**
* Generates Join Query for the given JoinCriteria
* @param criteriaJoin
* @return
*/
public static String joinToString(CriteriaJoin criteriaJoin) {
StringBuilder builder = new StringBuilder("LEFT OUTER JOIN ")
.append(criteriaJoin.getEntityClass().getName())
.append(" ")
.append(criteriaJoin.getAlias())
.append(" ")
.append(" ON ");
if(criteriaJoin.getJoinRelations().size() == 0) {
throw new RuntimeException("Not found any Join Relations in " + criteriaJoin.getAlias() + " Join Criteria ! ");
}
StringJoiner joiner = new StringJoiner(" AND ");
List<JoinRelation> relationList = criteriaJoin.getJoinRelations();
for(JoinRelation joinRelation: relationList) {
StringBuilder relationBuilder = new StringBuilder("\n")
.append(joinRelation.getRelationCriteria().getAlias())
.append(".")
.append(joinRelation.getRelationField())
.append("=")
.append(joinRelation.getJoinedCriteria().getAlias())
.append(".")
.append(joinRelation.getJoinedField());
joiner.add(relationBuilder.toString());
}
if(joiner.length() > 0) {
builder.append(joiner.toString());
}
return builder.toString();
}
}