SortOrder.java
/*
* Copyright 2019-2022 the original author or authors.
*
* Licensed 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.bremersee.comparator.model;
import static java.util.Objects.isNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElementRef;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlTransient;
import jakarta.xml.bind.annotation.XmlType;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
import lombok.EqualsAndHashCode;
/**
* The sort order is a list of sort order items.
*
* @author Christian Bremer
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "sortOrder")
@XmlType(name = "sortOrderType")
@JsonIgnoreProperties(ignoreUnknown = true)
@Schema(description = "The sort order.")
@EqualsAndHashCode
public class SortOrder implements Serializable {
@Serial
private static final long serialVersionUID = 1;
/**
* The constant DEFAULT_SEPARATOR.
*/
public static final String DEFAULT_SEPARATOR = ";";
@Schema(description = "The sort order items.")
@XmlElementRef
private final List<SortOrderItem> items = new ArrayList<>();
/**
* Instantiates an empty sort order.
*/
protected SortOrder() {
super();
}
/**
* Instantiates a new unmodifiable sort order.
*
* @param sortOrderItems the sort order items
*/
@JsonCreator
public SortOrder(@JsonProperty("items") Collection<? extends SortOrderItem> sortOrderItems) {
if (!isNull(sortOrderItems)) {
this.items.addAll(sortOrderItems);
}
}
/**
* Gets the unmodifiable list of sort order items.
*
* @return the list of sort order items
*/
public List<SortOrderItem> getItems() {
return Collections.unmodifiableList(items);
}
/**
* Checks whether the list of items is empty or not.
*
* @return {@code true} if the list of items is empty, otherwise {@code false}
*/
@XmlTransient
@JsonIgnore
public boolean isEmpty() {
return items.isEmpty();
}
/**
* Checks whether this sort order contains any entries. If there are entries, this is sorted,
* otherwise it is unsorted.
*
* @return {@code true} if the list of sort orders is not empty (aka sorted), otherwise
* {@code false}
*/
@XmlTransient
@JsonIgnore
public boolean isSorted() {
return !isEmpty();
}
/**
* Checks whether this sort order contains any entries. If there are no entries, this is unsorted,
* otherwise it is sorted.
*
* @return {@code true} if the list of sort orders is empty (aka unsorted), otherwise
* {@code false}
*/
@XmlTransient
@JsonIgnore
public boolean isUnsorted() {
return !isSorted();
}
/**
* Creates the sort order text of this ordering descriptions.
*
* <p>The syntax of the ordering description is
* <pre>
* fieldNameOrPath0,direction,case-handling,null-handling;fieldNameOrPath1,direction,case-handling,null-handling
* </pre>
*
* <p>For example
* <pre>
* created,desc;person.lastName,asc;person.firstName,asc
* </pre>
*
* @return the sort order text
*/
@JsonIgnore
@XmlTransient
public String getSortOrderText() {
return getSortOrderText(SortOrderTextSeparators.defaults());
}
/**
* Creates the sort order text of this ordering descriptions.
*
* <p>The syntax of the ordering description is
* <pre>
* fieldNameOrPath0,direction,case-handling,null-handling;fieldNameOrPath1,direction,case-handling,null-handling
* </pre>
*
* <p>For example
* <pre>
* created,desc;person.lastName,asc;person.firstName,asc
* </pre>
*
* @param separators the separators
* @return the sort order text
*/
public String getSortOrderText(SortOrderTextSeparators separators) {
String separator = Optional.ofNullable(separators)
.orElseGet(SortOrderTextSeparators::defaults)
.getChainSeparator();
return items.stream()
.map(item -> item.getSortOrderText(separators))
.collect(Collectors.joining(separator));
}
@Override
public String toString() {
return getSortOrderText();
}
/**
* From sort order text.
*
* @param source the sort order text
* @return the sort order
*/
public static SortOrder fromSortOrderText(String source) {
return fromSortOrderText(source, SortOrderTextSeparators.defaults());
}
/**
* From sort order text.
*
* @param source the sort order text
* @param separators the separators
* @return the sort order
*/
public static SortOrder fromSortOrderText(String source, SortOrderTextSeparators separators) {
if (isNull(source)) {
return unsorted();
}
String separator = Optional.ofNullable(separators)
.orElseGet(SortOrderTextSeparators::defaults)
.getChainSeparator();
return Optional.of(source.trim())
.map(text -> {
List<SortOrderItem> sortOrderItems = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(text, separator);
while (tokenizer.hasMoreTokens()) {
sortOrderItems.add(SortOrderItem
.fromSortOrderText(tokenizer.nextToken(), separators));
}
if (sortOrderItems.isEmpty()) {
sortOrderItems.add(new SortOrderItem());
}
return new SortOrder(sortOrderItems);
})
.orElseGet(SortOrder::new);
}
/**
* Creates new sort order with the given items.
*
* @param sortOrderItems the sort orders
* @return the sort order
*/
public static SortOrder by(SortOrderItem... sortOrderItems) {
return Optional.ofNullable(sortOrderItems)
.map(so -> new SortOrder(Arrays.asList(so)))
.orElseGet(SortOrder::new);
}
public static SortOrder unsorted() {
return new SortOrder();
}
}