de.metas.ui.web.window.model.DocumentQueryOrderBy.java Source code

Java tutorial

Introduction

Here is the source code for de.metas.ui.web.window.model.DocumentQueryOrderBy.java

Source

package de.metas.ui.web.window.model;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;

import de.metas.ui.web.window.datatypes.json.JSONNullValue;
import de.metas.util.Check;
import de.metas.util.GuavaCollectors;
import lombok.Builder;
import lombok.ToString;
import lombok.Value;

/*
 * #%L
 * metasfresh-webui-api
 * %%
 * Copyright (C) 2016 metas GmbH
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program. If not, see
 * <http://www.gnu.org/licenses/gpl-2.0.html>.
 * #L%
 */

@Value
public final class DocumentQueryOrderBy {
    public static final DocumentQueryOrderBy byFieldName(final String fieldName) {
        final boolean ascending = true;
        final boolean nullsLast = getDefaultNullsLastByAscending(ascending);
        return new DocumentQueryOrderBy(fieldName, ascending, nullsLast);
    }

    public static final DocumentQueryOrderBy byFieldName(final String fieldName, final boolean ascending) {
        final boolean nullsLast = getDefaultNullsLastByAscending(ascending);
        return new DocumentQueryOrderBy(fieldName, ascending, nullsLast);
    }

    /**
     * @param orderBysListStr Command separated field names. Use +/- prefix for ascending/descending. e.g. +C_BPartner_ID,-DateOrdered
     */
    public static final List<DocumentQueryOrderBy> parseOrderBysList(final String orderBysListStr) {
        if (Check.isEmpty(orderBysListStr, true)) {
            return ImmutableList.of();
        }

        return Splitter.on(',').trimResults().omitEmptyStrings().splitToList(orderBysListStr).stream()
                .map(orderByStr -> parseOrderBy(orderByStr)).collect(GuavaCollectors.toImmutableList());
    }

    /**
     * @param orderByStr field name with optional +/- prefix for ascending/descending. e.g. +C_BPartner_ID
     */
    private static final DocumentQueryOrderBy parseOrderBy(final String orderByStr) {
        if (orderByStr.charAt(0) == '+') {
            final String fieldName = orderByStr.substring(1);
            final boolean ascending = true;
            final boolean nullsLast = getDefaultNullsLastByAscending(ascending);
            return new DocumentQueryOrderBy(fieldName, ascending, nullsLast);
        } else if (orderByStr.charAt(0) == '-') {
            final String fieldName = orderByStr.substring(1);
            final boolean ascending = false;
            final boolean nullsLast = getDefaultNullsLastByAscending(ascending);
            return new DocumentQueryOrderBy(fieldName, ascending, nullsLast);
        } else {
            final String fieldName = orderByStr;
            final boolean ascending = true;
            final boolean nullsLast = getDefaultNullsLastByAscending(ascending);
            return new DocumentQueryOrderBy(fieldName, ascending, nullsLast);
        }
    }

    private static final boolean getDefaultNullsLastByAscending(final boolean ascending) {
        return true; // always nulls last
    }

    private final String fieldName;
    private final boolean ascending;
    private final boolean nullsLast;

    @Builder
    private DocumentQueryOrderBy(final String fieldName, final Boolean ascending, final Boolean nullsLast) {
        Check.assumeNotEmpty(fieldName, "fieldName is not empty");
        this.fieldName = fieldName;
        this.ascending = ascending != null ? ascending : true;
        this.nullsLast = nullsLast != null ? nullsLast : getDefaultNullsLastByAscending(this.ascending);
    }

    public DocumentQueryOrderBy copyOverridingFieldName(final String fieldName) {
        if (Objects.equals(this.fieldName, fieldName)) {
            return this;
        }
        return new DocumentQueryOrderBy(fieldName, ascending, nullsLast);
    }

    public <T> Comparator<T> asComparator(final FieldValueExtractor<T> fieldValueExtractor) {
        final Function<T, Object> keyExtractor = obj -> fieldValueExtractor.getFieldValue(obj, fieldName);
        Comparator<? super Object> keyComparator = ValueComparator.ofAscendingAndNullsLast(ascending, nullsLast);
        return Comparator.comparing(keyExtractor, keyComparator);
    }

    @FunctionalInterface
    public static interface FieldValueExtractor<T> {
        Object getFieldValue(T object, String fieldName);
    }

    @ToString
    private static final class ValueComparator implements Comparator<Object> {
        public static final ValueComparator ofAscendingAndNullsLast(final boolean ascending,
                final boolean nullsLast) {
            if (ascending) {
                return nullsLast ? ASCENDING_NULLS_LAST : ASCENDING_NULLS_FIRST;
            } else {
                return nullsLast ? DESCENDING_NULLS_LAST : DESCENDING_NULLS_FIRST;
            }
        }

        public static final transient ValueComparator ASCENDING_NULLS_FIRST = new ValueComparator(true, false);
        public static final transient ValueComparator ASCENDING_NULLS_LAST = new ValueComparator(true, true);
        public static final transient ValueComparator DESCENDING_NULLS_FIRST = new ValueComparator(false, false);
        public static final transient ValueComparator DESCENDING_NULLS_LAST = new ValueComparator(false, true);

        private final boolean ascending;
        private final boolean nullsLast;

        private ValueComparator(final boolean ascending, final boolean nullsLast) {
            this.ascending = ascending;
            this.nullsLast = nullsLast;
        }

        @Override
        public int compare(final Object o1, final Object o2) {
            if (o1 == o2) {
                return 0;
            } else if (o1 == null || o1 instanceof JSONNullValue) {
                return nullsLast ? +1 : -1;
            } else if (o2 == null || o2 instanceof JSONNullValue) {
                return nullsLast ? -1 : +1;
            } else if (o1 instanceof Comparable) {
                @SuppressWarnings("unchecked")
                final Comparable<Object> o1cmp = (Comparable<Object>) o1;
                return o1cmp.compareTo(o2) * (ascending ? +1 : -1);
            } else {
                return o1.toString().compareTo(o2.toString()) * (ascending ? +1 : -1);
            }
        }
    }
}