ValueComparator.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;
import java.util.Comparator;
import lombok.ToString;
import org.bremersee.comparator.model.SortOrder;
/**
* The value comparator extracts field value of the specified field name or path and uses the
* specified description (ascending or descending, case sensitive or insensitive and 'null is
* first') for sorting.
*
* @author Christian Bremer
*/
@ToString
public class ValueComparator implements Comparator<Object> {
private final ValueExtractor valueExtractor;
private final String field;
private final boolean asc;
private final boolean ignoreCase;
private final boolean nullIsFirst;
/**
* Instantiates a new value comparator.
*
* @param sortOrder the sort order (cannot be {@code null})
*/
public ValueComparator(SortOrder sortOrder) {
this(sortOrder.getField(),
sortOrder.isAsc(),
sortOrder.isIgnoreCase(),
sortOrder.isNullIsFirst(),
null);
}
/**
* Instantiates a new value comparator.
*
* @param sortOrder the sort order (cannot be {@code null})
* @param valueExtractor the value extractor (if it is {@code null}, a default will be used)
*/
public ValueComparator(SortOrder sortOrder, ValueExtractor valueExtractor) {
this(sortOrder.getField(),
sortOrder.isAsc(),
sortOrder.isIgnoreCase(),
sortOrder.isNullIsFirst(),
valueExtractor);
}
/**
* Instantiates a new value comparator.
*
* @param field the field name or path
* @param asc ascending or descending
* @param ignoreCase case insensitive or sensitive
* @param nullIsFirst null is first
*/
public ValueComparator(
String field,
boolean asc,
boolean ignoreCase,
boolean nullIsFirst) {
this(field, asc, ignoreCase, nullIsFirst, null);
}
/**
* Instantiates a new value comparator.
*
* @param field the field name or path
* @param asc ascending or descending
* @param ignoreCase case insensitive or sensitive
* @param nullIsFirst null is first
* @param valueExtractor a custom value extractor (if it is {@code null}, a default will be
* used)
*/
public ValueComparator(
String field,
boolean asc,
boolean ignoreCase,
boolean nullIsFirst,
ValueExtractor valueExtractor) {
this.field = field;
this.asc = asc;
this.ignoreCase = ignoreCase;
this.nullIsFirst = nullIsFirst;
this.valueExtractor = valueExtractor != null ? valueExtractor : new DefaultValueExtractor();
}
@Override
public int compare(Object o1, Object o2) {
final Object v1 = valueExtractor.findValue(o1, field);
final Object v2 = valueExtractor.findValue(o2, field);
if (v1 == null && v2 == null) {
return 0;
}
if (v1 == null) {
if (asc) {
return nullIsFirst ? -1 : 1;
} else {
return nullIsFirst ? 1 : -1;
}
}
if (v2 == null) {
if (asc) {
return nullIsFirst ? 1 : -1;
} else {
return nullIsFirst ? -1 : 1;
}
}
if (asc && v1 instanceof Comparable) {
if (ignoreCase && v1 instanceof String && v2 instanceof String) {
return ((String) v1).compareToIgnoreCase((String) v2);
} else {
//noinspection unchecked,rawtypes
return ((Comparable) v1).compareTo(v2);
}
} else if (!asc && v2 instanceof Comparable) {
if (ignoreCase && v1 instanceof String && v2 instanceof String) {
return ((String) v2).compareToIgnoreCase((String) v1);
} else {
//noinspection unchecked,rawtypes
return ((Comparable) v2).compareTo(v1);
}
}
throw new ComparatorException("Comparison of field '" + field + "' is not possible.");
}
}