package jpasearch.repository.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import jpasearch.domain.Identifiable;
import jpasearch.repository.query.selector.PropertySelector;
/**
* Helper to create a predicate out of {@link PropertySelector}s.
*/
@Named
@Singleton
public class ByPropertySelectorUtil {
private JpaUtil jpaUtil;
private MetamodelUtil metamodelUtil;
@SuppressWarnings("unchecked")
public <E> Predicate byPropertySelectors(Root<E> root, CriteriaBuilder builder, PropertySelector<? super E, ?> propertySelector) {
List<Predicate> predicates = new ArrayList<>();
if (metamodelUtil.isBoolean(root.getJavaType(), propertySelector.getPath())) {
byBooleanSelector(root, builder, predicates, (PropertySelector<? super E, Boolean>) propertySelector);
} else if (metamodelUtil.isString(root.getJavaType(), propertySelector.getPath())) {
byStringSelector(root, builder, predicates, (PropertySelector<? super E, String>) propertySelector);
} else {
byObjectSelector(root, builder, predicates, propertySelector);
}
if (propertySelector.isOrMode()) {
return jpaUtil.orPredicate(builder, predicates);
} else {
return jpaUtil.andPredicate(builder, predicates);
}
}
private <E> void byBooleanSelector(Root<E> root, CriteriaBuilder builder, List<Predicate> predicates, PropertySelector<? super E, Boolean> selector) {
if (selector.isNotEmpty()) {
List<Predicate> selectorPredicates = new ArrayList<>();
for (Boolean selection : selector.getSelected()) {
Path<Boolean> path = jpaUtil.getPath(root, selector.getPath());
if (selection == null) {
selectorPredicates.add(builder.isNull(path));
} else {
selectorPredicates.add(selection ? builder.isTrue(path) : builder.isFalse(path));
}
}
Predicate predicate;
if (selector.isOrMode()) {
predicate = jpaUtil.orPredicate(builder, selectorPredicates);
if (selector.isNotMode()) {
predicate = builder.not(predicate);
}
} else {
predicate = jpaUtil.andPredicate(builder, selectorPredicates);
if (selector.isNotMode()) {
predicate = builder.not(predicate);
}
}
predicates.add(predicate);
}
}
private <E> void byStringSelector(Root<E> root, CriteriaBuilder builder, List<Predicate> predicates, PropertySelector<? super E, String> selector) {
if (selector.isNotEmpty()) {
Path<String> path = null;
List<Predicate> selectorPredicates = new ArrayList<>();
List<String> selected = selector.getSelected();
if (selector.getSelected().contains(null)) {
path = jpaUtil.getPath(root, selector.getPath());
selected = new ArrayList<>(selector.getSelected());
selected.remove(null);
selectorPredicates.add(builder.isNull(path));
}
Predicate predicate;
if (selector.isOrMode()) {
// re-use created pat if it exists in 'or' mode : only one join
if (path == null) {
path = jpaUtil.getPath(root, selector.getPath());
}
for (String selection : selected) {
selectorPredicates.add(jpaUtil.stringPredicate(path, selection, selector.getSearchMode(), selector.isCaseSensitive(), builder));
}
predicate = jpaUtil.orPredicate(builder, selectorPredicates);
if (selector.isNotMode()) {
predicate = builder.not(predicate);
}
} else {
for (String selection : selected) {
// explicitly create new path for new join in 'and' mode
path = jpaUtil.getPath(root, selector.getPath());
selectorPredicates.add(jpaUtil.stringPredicate(path, selection, selector.getSearchMode(), selector.isCaseSensitive(), builder));
}
predicate = jpaUtil.andPredicate(builder, selectorPredicates);
if (selector.isNotMode()) {
predicate = builder.not(predicate);
}
}
predicates.add(predicate);
}
}
private <E> void byObjectSelector(Root<E> root, CriteriaBuilder builder, List<Predicate> predicates, PropertySelector<? super E, ?> selector) {
if (selector.isNotEmpty()) {
if (selector.isOrMode()) {
byObjectOrModeSelector(root, builder, predicates, selector);
} else {
byObjectAndModeSelector(root, builder, predicates, selector);
}
} else if (selector.isNotIncludingNullSet()) {
predicates.add(builder.isNotNull(jpaUtil.getPath(root, selector.getPath())));
}
}
private <E> void byObjectOrModeSelector(Root<E> root, CriteriaBuilder builder, List<Predicate> predicates, PropertySelector<? super E, ?> selector) {
List<Predicate> selectorPredicates = new ArrayList<>();
Path<?> path = jpaUtil.getPath(root, selector.getPath());
List<?> selected = selector.getSelected();
if (selector.getSelected().contains(null)) {
selected = new ArrayList<>(selector.getSelected());
selected.remove(null);
selectorPredicates.add(builder.isNull(path));
}
if (!selected.isEmpty()) {
if (selected.get(0) instanceof Identifiable) {
List<Serializable> ids = new ArrayList<>();
for (Object selection : selected) {
ids.add(((Identifiable<?>) selection).getId());
}
path = path.get("id");
selected = ids;
}
if (selected.size() > 1) {
selectorPredicates.add(path.in(selected));
} else {
selectorPredicates.add(builder.equal(path, selected.get(0)));
}
}
Predicate predicate = jpaUtil.orPredicate(builder, selectorPredicates);
if (selector.isNotMode()) {
predicate = builder.not(predicate);
}
predicates.add(predicate);
}
private <E> void byObjectAndModeSelector(Root<E> root, CriteriaBuilder builder, List<Predicate> predicates, PropertySelector<? super E, ?> selector) {
List<Predicate> selectorPredicates = new ArrayList<>();
List<?> selected = selector.getSelected();
if (selector.getSelected().contains(null)) {
selected = new ArrayList<>(selector.getSelected());
selected.remove(null);
selectorPredicates.add(builder.isNull(jpaUtil.getPath(root, selector.getPath())));
}
for (Object selection : selector.getSelected()) {
Path<?> path = jpaUtil.getPath(root, selector.getPath());
if (selection instanceof Identifiable) {
selectorPredicates.add(builder.equal(path.get("id"), ((Identifiable<?>) selection).getId()));
} else {
selectorPredicates.add(builder.equal(path, selection));
}
}
Predicate predicate = jpaUtil.andPredicate(builder, selectorPredicates);
if (selector.isNotMode()) {
predicate = builder.not(predicate);
}
predicates.add(predicate);
}
@Inject
public void setJpaUtil(JpaUtil jpaUtil) {
this.jpaUtil = jpaUtil;
}
@Inject
public void setMetamodelUtil(MetamodelUtil metamodelUtil) {
this.metamodelUtil = metamodelUtil;
}
}