package fr.openwide.core.jpa.more.business.sort; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.bindgen.Binding; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.ComparableExpressionBase; import fr.openwide.core.jpa.more.business.sort.ISort.NullSortValue; import fr.openwide.core.jpa.more.business.sort.ISort.SortNull; import fr.openwide.core.jpa.more.business.sort.ISort.SortOrder; public final class SortUtils { private SortUtils() { } // Common private static boolean isReverse(ISort<?> sort, SortOrder order) { return SortOrder.DESC == sort.getDefaultOrder().asDefaultFor(order); } public static <S, T extends ISort<S>> List<S> collectSortFields(Map<T, SortOrder> sortsMap) { List<S> sortFields = Lists.newArrayList(); if (sortsMap != null) { for (Entry<T, SortOrder> sortOrderEntry : sortsMap.entrySet()) { sortFields.addAll(sortOrderEntry.getKey().getSortFields(sortOrderEntry.getValue())); } } return sortFields; } // Hibernate Search @SafeVarargs public static <T extends ISort<?>> Map<T, SortOrder> composite(T first, T ... rest) { return composite(Lists.asList(first, rest)); } public static <T extends ISort<?>> Map<T, SortOrder> composite(Collection<T> sorts) { Map<T, SortOrder> map = Maps.newLinkedHashMap(); for (T sort : sorts) { map.put(sort, sort.getDefaultOrder()); } return map; } public static <T extends ISort<?>> void appendTo(Map<T, SortOrder> dst, Map<? extends T, SortOrder> addedMap) { for (Map.Entry<? extends T, SortOrder> addedEntry : addedMap.entrySet()) { T sort = addedEntry.getKey(); if (!dst.containsKey(sort)) { SortOrder sortOrder = addedEntry.getValue(); dst.put(sort, sortOrder); } } } public static SortField luceneSortField(ISort<SortField> sort, SortOrder order, SortField.Type type, Binding<?> binding, String ... otherFieldParts) { List<String> fieldParts = Lists.asList(binding.getPath(), otherFieldParts); String fieldName = Joiner.on(".").join(fieldParts); return luceneSortField(sort, order, type, fieldName); } public static SortField luceneSortField(ISort<SortField> sort, SortOrder order, SortField.Type type, String fieldName) { return new SortField(fieldName, type, isReverse(sort, order)); } /** * @deprecated Use {@link #luceneStringSortField(ISort, SortOrder, String, NullSortValue)} instead. * This method sorts nulls in the same order independently from the given SortOrder, which probably is a bug. */ @Deprecated public static SortField luceneStringSortField(ISort<SortField> sort, SortOrder order, String fieldName, SortNull sortNull) { return getStringSortField(fieldName, isReverse(sort, order), SortNull.NULL_LAST.equals(sortNull), SortNull.NULL_FIRST.equals(sortNull)); } public static SortField luceneStringSortField(ISort<SortField> sort, SortOrder order, String fieldName, NullSortValue sortNull) { SortOrder defaultedOrder = sort.getDefaultOrder().asDefaultFor(order); return getStringSortField(fieldName, isReverse(sort, order), sortNull.isLast(defaultedOrder), sortNull.isFirst(defaultedOrder)); } public static SortField luceneLongSortField(ISort<SortField> sort, SortOrder order, String fieldName, NullSortValue sortNull) { SortOrder defaultedOrder = sort.getDefaultOrder().asDefaultFor(order); return getLongSortField(fieldName, isReverse(sort, order), sortNull.isLast(defaultedOrder), sortNull.isFirst(defaultedOrder)); } @SafeVarargs public static <T extends ISort<SortField>> Sort getLuceneSortWithDefaults( Map<T, SortOrder> sortsMap, T firstDefaultSort, T ... otherDefaultSorts) { return getLuceneSortWithDefaults(sortsMap, Lists.asList(firstDefaultSort, otherDefaultSorts)); } public static <T extends ISort<SortField>> Sort getLuceneSortWithDefaults( Map<T, SortOrder> sortsMap, List<T> list) { List<SortField> sortFields = collectSortFields(sortsMap); for (T defaultSort : list) { sortFields.addAll(defaultSort.getSortFields(defaultSort.getDefaultOrder())); } return new Sort(sortFields.toArray(new SortField[sortFields.size()])); } private static <T extends ISort<? extends C>, C extends Comparator<?>> List<C> collectComparators(Map<T, SortOrder> sortsMap) { List<C> sortFields = Lists.newArrayList(); if (sortsMap != null) { for (Entry<T, SortOrder> sortOrderEntry : sortsMap.entrySet()) { sortFields.addAll(sortOrderEntry.getKey().getSortFields(sortOrderEntry.getValue())); } } return sortFields; } @SafeVarargs public static <S extends ISort<? extends Comparator<? super T>>, T> Ordering<T> getComparatorSortWithDefaults( Map<? extends S, SortOrder> sortsMap, S firstDefaultSort, S ... otherDefaultSorts) { List<Comparator<? super T>> sortFields = SortUtils.collectComparators(sortsMap); for (S defaultSort : Lists.asList(firstDefaultSort, otherDefaultSorts)) { sortFields.addAll(defaultSort.getSortFields(defaultSort.getDefaultOrder())); } return Ordering.compound(sortFields); } /** * Comes from Sorting of Solr * * Returns a {@link SortField} for a string field. * If nullLast and nullFirst are both false, then default lucene string sorting is used where * null strings sort first in an ascending sort, and last in a descending sort. * * @param fieldName the name of the field to sort on * @param reverse true for a reverse (desc) sort * @param nullLast true if null should come last, regardless of sort order * @param nullFirst true if null should come first, regardless of sort order * @return SortField */ public static SortField getStringSortField(String fieldName, boolean reverse, boolean nullLast, boolean nullFirst) { if (nullFirst && nullLast) { throw new IllegalArgumentException("Cannot specify missing values as both first and last"); } SortField sortField = new SortField(fieldName, SortField.Type.STRING, reverse); // 4 cases: // missingFirst / forward: default lucene behavior // missingFirst / reverse: set sortMissingLast // missingLast / forward: set sortMissingLast // missingLast / reverse: default lucene behavior if (nullFirst && reverse) { sortField.setMissingValue(SortField.STRING_LAST); } else if (nullLast && !reverse) { sortField.setMissingValue(SortField.STRING_LAST); } return sortField; } public static SortField getLongSortField(String fieldName, boolean reverse, boolean nullLast, boolean nullFirst) { if (nullFirst && nullLast) { throw new IllegalArgumentException("Cannot specify missing values as both first and last"); } SortField sortField = new SortField(fieldName, SortField.Type.LONG, reverse); // 4 cases: // missingFirst / forward: default lucene behavior // missingFirst / reverse: set sortMissingLast // missingLast / forward: set sortMissingLast // missingLast / reverse: default lucene behavior if (nullFirst && reverse) { sortField.setMissingValue(Long.MAX_VALUE); } else if (nullLast && !reverse) { sortField.setMissingValue(Long.MAX_VALUE); } return sortField; } // JPA public static <T extends Comparable<?>> OrderSpecifier<T> orderSpecifier(ISort<OrderSpecifier<?>> sort, SortOrder order, ComparableExpressionBase<T> expressionBase) { if (isReverse(sort, order)) { return expressionBase.desc(); } else { return expressionBase.asc(); } } @SafeVarargs public static <T extends ISort<OrderSpecifier<?>>> List<OrderSpecifier<?>> getOrderSpecifierWithDefaults( Map<T, SortOrder> sortsMap, T firstDefaultSort, T ... otherDefaultSorts) { return getOrderSpecifierWithDefaults(sortsMap, Lists.asList(firstDefaultSort, otherDefaultSorts)); } public static <T extends ISort<OrderSpecifier<?>>> List<OrderSpecifier<?>> getOrderSpecifierWithDefaults( Map<T, SortOrder> sortsMap, List<T> list) { List<OrderSpecifier<?>> sortFields = collectSortFields(sortsMap); for (T defaultSort : list) { sortFields.addAll(defaultSort.getSortFields(defaultSort.getDefaultOrder())); } return sortFields; } // SQL public static String orderSpecifier(ISort<String> sort, SortOrder order, String expression) { if (isReverse(sort, order)) { return new StringBuilder().append(expression).append(" DESC").toString(); } else { return new StringBuilder().append(expression).append(" ASC").toString(); } } @SafeVarargs public static <T extends ISort<String>> List<String> getSQLOrderSpecifierWithDefaults( Map<T, SortOrder> sortsMap, T firstDefaultSort, T ... otherDefaultSorts) { return getSQLOrderSpecifierWithDefaults(sortsMap, Lists.asList(firstDefaultSort, otherDefaultSorts)); } public static <T extends ISort<String>> List<String> getSQLOrderSpecifierWithDefaults( Map<T, SortOrder> sortsMap, List<T> list) { List<String> sortFields = collectSortFields(sortsMap); for (T defaultSort : list) { sortFields.addAll(defaultSort.getSortFields(defaultSort.getDefaultOrder())); } return sortFields; } }