/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.ambari.server.api.query;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;
import org.apache.ambari.server.controller.spi.SortRequest;
import org.apache.ambari.server.controller.spi.SortRequestProperty;
import org.apache.commons.lang.ObjectUtils;
/**
* The {@link JpaSortBuilder} class is used to convert and Ambari
* {@link SortRequest} and list of {@link SortRequestProperty} into a JPA
* {@link Order} list. This can then be given to a {@link CriteriaBuilder} to
* provide sorting in the JPA layer during a query.
* <p/>
* This classes uses the {@link JpaPredicateVisitor} for the entity being
* queried in order to convert the entity fields into {@link Path} expressions.
*/
public class JpaSortBuilder<T> {
/**
* Constructor.
*
*/
public JpaSortBuilder() {
}
/**
* Builds the list of sort orders based on the supplied request and JPA
* predicate visitor.
*
* @param sortRequest
* the Ambari sort request properties to turn into a JPA sort
* request. If {@code null} or the {@link SortRequestProperty} list
* is null, an empty list is returned.
* @param visitor
* a visitor that knows how to convert the Ambari properties into
* {@link SingularAttribute} (not {@code null}).
* @return a list of sorts or an empty list if none (never {@code null}).
*/
public List<Order> buildSortOrders(SortRequest sortRequest,
JpaPredicateVisitor<T> visitor) {
if (null == sortRequest || null == sortRequest.getProperties()) {
return Collections.emptyList();
}
CriteriaBuilder builder = visitor.getCriteriaBuilder();
List<SortRequestProperty> sortProperties = sortRequest.getProperties();
List<Order> sortOrders = new ArrayList<>(sortProperties.size());
for (SortRequestProperty sort : sortProperties) {
String propertyId = sort.getPropertyId();
List<? extends SingularAttribute<?, ?>> singularAttributes = visitor.getPredicateMapping(propertyId);
if (null == singularAttributes || singularAttributes.size() == 0) {
continue;
}
Path<?> path = null;
for (SingularAttribute<?, ?> singularAttribute : singularAttributes) {
if (null == path) {
CriteriaQuery<T> query = visitor.getCriteriaQuery();
Set<Root<?>> roots = query.getRoots();
// if there are existing roots; use the existing roots to prevent more
// roots from being added potentially causing a cartesian product
// where we don't want one
if (null != roots && !roots.isEmpty()) {
Iterator<Root<?>> iterator = roots.iterator();
while (iterator.hasNext()) {
Root<?> root = iterator.next();
Class<?> visitorEntityClass = visitor.getEntityClass();
if (ObjectUtils.equals(visitorEntityClass, root.getJavaType())
|| ObjectUtils.equals(visitorEntityClass, root.getModel().getJavaType())) {
path = root.get(singularAttribute.getName());
break;
}
}
}
// no roots exist already which match this entity class, create a new
// path
if (null == path) {
path = query.from(visitor.getEntityClass()).get(singularAttribute.getName());
}
} else {
path = path.get(singularAttribute.getName());
}
}
Order sortOrder = null;
if (sort.getOrder() == org.apache.ambari.server.controller.spi.SortRequest.Order.ASC) {
sortOrder = builder.asc(path);
} else {
sortOrder = builder.desc(path);
}
sortOrders.add(sortOrder);
}
return sortOrders;
}
}