package jpasearch.repository.util;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.toArray;
import static com.google.common.collect.Lists.newArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.FetchParent;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Bindable;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import jpasearch.domain.Identifiable;
import jpasearch.repository.query.SearchMode;
import jpasearch.repository.query.SearchParameters;
@Named
@Singleton
public class JpaUtil {
private MetamodelUtil metamodelUtil;
public Predicate andPredicate(CriteriaBuilder builder, Predicate... predicatesNullAllowed) {
return andPredicate(builder, Arrays.asList(predicatesNullAllowed));
}
public Predicate orPredicate(CriteriaBuilder builder, Predicate... predicatesNullAllowed) {
return orPredicate(builder, Arrays.asList(predicatesNullAllowed));
}
public Predicate andPredicate(CriteriaBuilder builder, Iterable<Predicate> predicatesNullAllowed) {
List<Predicate> predicates = newArrayList(filter(predicatesNullAllowed, notNull()));
if ((predicates == null) || predicates.isEmpty()) {
return null;
} else if (predicates.size() == 1) {
return predicates.get(0);
} else {
return builder.and(toArray(predicates, Predicate.class));
}
}
public Predicate orPredicate(CriteriaBuilder builder, Iterable<Predicate> predicatesNullAllowed) {
List<Predicate> predicates = newArrayList(filter(predicatesNullAllowed, notNull()));
if ((predicates == null) || predicates.isEmpty()) {
return null;
} else if (predicates.size() == 1) {
return predicates.get(0);
} else {
return builder.or(toArray(predicates, Predicate.class));
}
}
public <E> Predicate stringPredicate(Expression<String> path, Object attrValue, SearchMode searchMode, boolean caseSensitive, CriteriaBuilder builder) {
if (!caseSensitive) {
path = builder.lower(path);
attrValue = ((String) attrValue).toLowerCase(Locale.FRANCE);
}
switch (searchMode) {
case EQUALS:
return builder.equal(path, attrValue);
case ENDING_LIKE:
return builder.like(path, "%" + attrValue);
case STARTING_LIKE:
return builder.like(path, attrValue + "%");
case ANYWHERE:
return builder.like(path, "%" + attrValue + "%");
case LIKE:
// assume user provide the wild cards
return builder.like(path, (String) attrValue);
default:
throw new IllegalStateException("expecting a search mode!");
}
}
public <E, F> Path<F> getPath(Root<E> root, String path) {
return getPath(root, metamodelUtil.toAttributes(root.getJavaType(), path));
}
@SuppressWarnings("unchecked")
public <E, F> Path<F> getPath(Root<E> root, List<Attribute<?, ?>> attributes) {
Path<?> path = root;
for (Attribute<?, ?> attribute : attributes) {
boolean found = false;
if (path instanceof FetchParent) {
for (Fetch<E, ?> fetch : ((FetchParent<?, E>) path).getFetches()) {
if (attribute.getName().equals(fetch.getAttribute().getName()) && (fetch instanceof Join<?, ?>)) {
path = (Join<E, ?>) fetch;
found = true;
break;
}
}
}
if (!found) {
if ((attributes.indexOf(attribute) != (attributes.size() - 1)) && (attribute instanceof Bindable)
&& Identifiable.class.isAssignableFrom(((Bindable<?>) attribute).getBindableJavaType()) && (path instanceof From)) {
path = ((From<?, ?>) path).join(attribute.getName(), JoinType.LEFT);
} else {
path = path.get(attribute.getName());
}
}
}
return (Path<F>) path;
}
public void applyPagination(Query query, SearchParameters<?> sp) {
if (sp.getFirstResult() > 0) {
query.setFirstResult(sp.getFirstResult());
}
if (sp.getMaxResults() > 0) {
query.setMaxResults(sp.getMaxResults());
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <E> void fetches(SearchParameters<E> sp, Root<E> root) {
for (jpasearch.repository.query.Path<E, ?> path : sp.getFetches()) {
FetchParent<?, ?> from = root;
for (Attribute<?, ?> arg : metamodelUtil.toAttributes(root.getJavaType(), path.getPath())) {
boolean found = false;
for (Fetch<?, ?> fetch : from.getFetches()) {
if (arg.equals(fetch.getAttribute())) {
from = fetch;
found = true;
break;
}
}
if (!found) {
if (arg instanceof PluralAttribute) {
from = from.fetch((PluralAttribute) arg, JoinType.LEFT);
} else {
from = from.fetch((SingularAttribute) arg, JoinType.LEFT);
}
}
}
}
}
@Inject
public void setMetamodelUtil(MetamodelUtil metamodelUtil) {
this.metamodelUtil = metamodelUtil;
}
}