diff --git a/account/src/main/java/de/muehlencord/shared/account/business/AbstractController.java b/account/src/main/java/de/muehlencord/shared/account/business/AbstractController.java index 084287d..5897c94 100644 --- a/account/src/main/java/de/muehlencord/shared/account/business/AbstractController.java +++ b/account/src/main/java/de/muehlencord/shared/account/business/AbstractController.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Locale; +import java.util.Map; import javax.ejb.Lock; import javax.ejb.LockType; import javax.ejb.TransactionAttribute; @@ -32,11 +34,14 @@ import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Order; +import javax.persistence.criteria.Path; +import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.metamodel.IdentifiableType; import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.SingularAttribute; import javax.transaction.Transactional; +import org.apache.shiro.util.StringUtils; /** * @@ -58,6 +63,130 @@ public abstract class AbstractController { this.entityClass = clazz; } + protected Predicate getFilterCondition(CriteriaBuilder cb, Root root, Map filters) { + return getFilterCondition(cb, root, filters, null); + } + + protected Predicate getFilterCondition(CriteriaBuilder cb, Root root, Map filters, Map excludeFilters) { + Predicate filterCondition = null; + filterCondition = getFilterCondition(filterCondition, cb, root, filters, true); + filterCondition = getFilterCondition(filterCondition, cb, root, excludeFilters, false); + return filterCondition; + } + + protected Predicate addFilterCondition(CriteriaBuilder cb, Predicate filterCondition, Predicate addCondition) { + if (addCondition == null) { + return filterCondition; + } + if (filterCondition == null) { + filterCondition = addCondition; + } else { + filterCondition = cb.and(filterCondition, addCondition); + } + return filterCondition; + } + + /** + * extends the given filterCondition by the addtional filters + * + * @param filterCondition the current filter condition + * @param cb the criteria builder to use + * @param root the root of the object to search for + * @param filters the filters to apply + * @param include if set to true, the filter is used as include filter + * (equals, in). If set to false, the filter is inverted and used as exclude + * filter (not equals, not in etc) + * @return + */ + protected Predicate getFilterCondition(Predicate filterCondition, CriteriaBuilder cb, Root root, Map filters, boolean include) { + String wildCard = "%"; + if (filters != null) { + for (Map.Entry filter : filters.entrySet()) { + if (!"".equals(filter.getValue())) { + Path path = getPathElement(root, filter); + + // check for differnt types + // 1st String, either from Enum Status or from other free text string + if (String.class.equals(filter.getValue().getClass())) { + switch (filter.getKey()) { + default: + String filterValue = filter.getValue().toString(); + Predicate predicate; + if (filterValue.equals("{NULL}")) { + if (include) { + predicate = cb.isNull(path); + } else { + predicate = cb.isNotNull(path); + } + } else { + filterValue = filterValue.trim(); + filterValue = filterValue.replace("?", "_"); + filterValue = filterValue.replace("*", "%"); + + String[] values = filterValue.split("\\s+"); // split by whitespaces + Predicate[] partSearchPredicates = new Predicate[values.length]; + for (int i = 0; i < values.length; i++) { + String value = wildCard + values[i] + wildCard; + if (include) { + partSearchPredicates[i] = cb.like(cb.upper(path), value.toUpperCase(Locale.US), '\\'); + } else { + partSearchPredicates[i] = cb.notLike(cb.upper(path), value.toUpperCase(Locale.US), '\\'); + } + } + predicate = cb.and(partSearchPredicates); // all parts must be available + } + filterCondition = addFilterCondition(cb, filterCondition, predicate); + } + } // 2nd for arrays, received from e.g. project selections + else if (filter.getValue().getClass().isArray()) { + Predicate condition = null; + Object[] values = (Object[]) filter.getValue(); + if (values.length > 0) { + for (Object value : values) { + if (include) { + Predicate equalPredicate = cb.equal(path, value); + if (condition == null) { + condition = equalPredicate; + } else { + condition = cb.or(condition, equalPredicate); + } + } else { + Predicate equalPredicate = cb.notEqual(path, value); + if (condition == null) { + condition = equalPredicate; + } else { + condition = cb.and(condition, equalPredicate); + } + } + } + filterCondition = addFilterCondition(cb, filterCondition, condition); + } + } else { // at last object comparison + if (include) { + filterCondition = addFilterCondition(cb, filterCondition, cb.equal(path, filter.getValue())); + } else { + filterCondition = addFilterCondition(cb, filterCondition, cb.notEqual(path, filter.getValue())); + } + } + } + } + } + return filterCondition; + } + + private Path getPathElement(Root root, Map.Entry filter) { + String[] pathElements = StringUtils.split(filter.getKey(), '.'); + Path path = null; + for (String element : pathElements) { + if (path == null) { + path = root.get(element); + } else { + path = path.get(element); + } + } + return path; + } + public void applyUpdateableChanges(Updateable updateable, boolean onCreate) throws ControllerException { if (onCreate) { updateable.setCreatedBy(account.getUsername()); @@ -130,6 +259,23 @@ public abstract class AbstractController { return query.getResultList(); } + @Lock(LockType.READ) + public List find(Map filters, List orderFields) { + final CriteriaBuilder cb = em.getCriteriaBuilder(); + final CriteriaQuery criteria = cb.createQuery(entityClass); + + final Root r = criteria.from(entityClass); + Predicate filterCondition = getFilterCondition(cb, r, filters); + if (filterCondition != null) { + criteria.where(filterCondition); + } + List orderList = new ArrayList<>(); + orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field)))); + final TypedQuery query = em.createQuery(criteria.orderBy(orderList)); + + return query.getResultList(); + } + /** * returns null, if the list is empty or null itself. Returns the one * element if there is exactly one element in the list. Otherwise an