/**
* Licensed to the Austrian Association for Software Tool Integration (AASTI)
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. The AASTI licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openengsb.core.edb.jpa.internal.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import org.openengsb.core.api.model.QueryRequest;
import org.openengsb.core.edb.api.EDBConstants;
import org.openengsb.core.edb.jpa.internal.JPAEntry;
import org.openengsb.core.edb.jpa.internal.JPAObject;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* Converts a QueryRequest into a CriteriaQuery.
*
*/
public class QueryRequestCriteriaBuilder {
private final CriteriaBuilder builder;
private final QueryRequest request;
public QueryRequestCriteriaBuilder(QueryRequest request, CriteriaBuilder builder) {
this.builder = builder;
this.request = request;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public CriteriaQuery<JPAObject> buildQuery() {
CriteriaQuery<JPAObject> criteriaQuery = builder.createQuery(JPAObject.class);
criteriaQuery.distinct(!request.isAndJoined());
Root from = criteriaQuery.from(JPAObject.class);
Subquery<Long> subquery = criteriaQuery.subquery(Long.class);
Root subFrom = subquery.from(JPAObject.class);
Expression<Long> maxExpression = builder.max(subFrom.get("timestamp"));
subquery.select(maxExpression);
Predicate p1 = builder.equal(subFrom.get("oid"), from.get("oid"));
Predicate p2 = builder.le(subFrom.get("timestamp"), request.getTimestamp());
subquery.where(builder.and(p1, p2));
List<Predicate> predicates = new ArrayList<>();
if (request.getContextId() != null) {
predicates.add(builder.like(from.get("oid"), request.getContextId() + "/%"));
}
predicates.add(builder.notEqual(from.get("isDeleted"), !request.isDeleted()));
predicates.add(builder.equal(from.get("timestamp"), subquery));
if (request.getModelClassName() != null) {
Subquery<JPAEntry> subquery2 =
buildJPAEntrySubquery(EDBConstants.MODEL_TYPE, request.getModelClassName(), from, criteriaQuery);
predicates.add(builder.exists(subquery2));
}
predicates.add(convertParametersToPredicate(from, criteriaQuery));
criteriaQuery.where(Iterables.toArray(predicates, Predicate.class));
return criteriaQuery;
}
/**
* Converts a query request parameter map for a query operation into a list of predicates which need to be added to
* the criteria query.
*/
@SuppressWarnings({ "unchecked" })
private Predicate convertParametersToPredicate(Root<?> from, CriteriaQuery<?> query) {
List<Predicate> predicates = new ArrayList<>();
for (Map.Entry<String, Set<Object>> value : request.getParameters().entrySet()) {
Subquery<JPAEntry> subquery = buildJPAEntrySubquery(value.getKey(), value.getValue(), from, query);
predicates.add(builder.exists(subquery));
}
if (request.isAndJoined()) {
return builder.and(Iterables.toArray(predicates, Predicate.class));
} else {
return builder.or(Iterables.toArray(predicates, Predicate.class));
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Subquery buildJPAEntrySubquery(String key, Object value, Root<?> from, CriteriaQuery<?> query) {
Subquery<JPAEntry> subquery = query.subquery(JPAEntry.class);
Root subFrom = subquery.from(JPAEntry.class);
subquery.select(subFrom);
Predicate ownerPredicate = builder.equal(from, subFrom.get("owner"));
Predicate keyPredicate = builder.like(subFrom.get("key"), key);
Predicate valuePredicate =
value instanceof Set ? buildValuePredicate((Set<Object>) value, subFrom) : builder.equal(
subFrom.get("value"),
value);
subquery.where(builder.and(ownerPredicate, keyPredicate, valuePredicate));
return subquery;
}
private Predicate buildValuePredicate(Set<Object> values, Root<?> subFrom) {
Expression<String> expression = subFrom.get("value");
if (!request.isCaseSensitive()) {
expression = builder.lower(expression);
}
List<Predicate> valuePredicates = Lists.newArrayList();
for (Object obj : values) {
String caseCheckedValue = getCaseCheckedValue(obj, request.isCaseSensitive());
valuePredicates.add(request.isWildcardAware() ? builder.like(expression, caseCheckedValue) : builder
.equal(expression, caseCheckedValue));
}
if (request.isAndJoined()) {
return builder.and(Iterables.toArray(valuePredicates, Predicate.class));
} else {
return builder.or(Iterables.toArray(valuePredicates, Predicate.class));
}
}
private String getCaseCheckedValue(Object object, boolean caseSensitive) {
return caseSensitive ? object.toString() : object.toString().toLowerCase();
}
}