All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metreeca.json.ValueComparator Maven / Gradle / Ivy

/*
 * Copyright © 2013-2021 Metreeca srl
 *
 * 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 com.metreeca.json;

import org.eclipse.rdf4j.model.*;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.model.vocabulary.XSD;

import java.io.Serializable;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Comparator;
import java.util.Objects;
import java.util.function.*;

final class ValueComparator implements Comparator, Serializable {

	private static final long serialVersionUID=1888795007683597342L;


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	@Override public int compare(final Value x, final Value y) {
		return Objects.equals(x, y) ? 0

				: x == null ? -1
				: y == null ? 1

				: x.isBNode() ? y.isBNode() ? compare((BNode)x, (BNode)y) : -1
				: y.isBNode() ? 1

				: x.isIRI() ? y.isIRI() ? compare((IRI)x, (IRI)y) : -1
				: y.isIRI() ? 1

				: x.isLiteral() ? y.isLiteral() ? compare((Literal)x, (Literal)y) : -1
				: y.isLiteral() ? 1

				: x.isTriple() ? y.isTriple() ? compare((Triple)x, (Triple)y) : -1
				: y.isTriple() ? 1

				: x.stringValue().compareTo(y.stringValue());
	}


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	private int compare(final BNode x, final BNode y) {
		return x.getID().compareTo(y.getID());
	}

	private int compare(final IRI x, final IRI y) {
		return x.stringValue().compareTo(y.stringValue());
	}

	private int compare(final Literal x, final Literal y) {
		return isBoolean(x) ? isBoolean(y) ? bool(x, y) : -1
				: isBoolean(y) ? 1

				: isNumeric(x) ? isNumeric(y) ? numeric(x, y) : -1
				: isNumeric(y) ? 1

				: isTemporal(x) ? isTemporal(y) ? temporal(x, y) : -1
				: isTemporal(y) ? 1

				: isDuration(x) ? isDuration(y) ? duration(x, y) : -1
				: isDuration(y) ? 1

				: isPlain(x) ? isPlain(y) ? plain(x, y) : -1
				: isPlain(y) ? 1

				: isTagged(x) ? isTagged(y) ? tagged(x, y) : -1
				: isTagged(y) ? 1

				: typed(x, y);
	}

	private int compare(final Triple x, final Triple y) {
		return chain(compare(x.getSubject(), y.getSubject()), () -> chain(
				compare(x.getPredicate(), y.getPredicate()), () ->
						compare(x.getObject(), y.getObject())
				)
		);
	}


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	private boolean isBoolean(final Literal literal) {
		return XSD.BOOLEAN.equals(literal.getDatatype());
	}

	private boolean isNumeric(final Literal literal) {
		return XSD.Datatype.from(literal.getDatatype()).map(XSD.Datatype::isNumericDatatype).orElse(false);
	}

	private boolean isTemporal(final Literal literal) {
		return XSD.Datatype.from(literal.getDatatype()).map(XSD.Datatype::isCalendarDatatype).orElse(false);
	}

	private boolean isDuration(final Literal literal) {
		return XSD.Datatype.from(literal.getDatatype()).map(XSD.Datatype::isDurationDatatype).orElse(false);
	}

	private boolean isPlain(final Literal literal) {
		return XSD.STRING.equals(literal.getDatatype());
	}

	private boolean isTagged(final Literal literal) {
		return RDF.LANGSTRING.equals(literal.getDatatype());
	}


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	private int bool(final Literal x, final Literal y) {
		return comparable(x, y, Literal::booleanValue);
	}

	private int numeric(final Literal x, final Literal y) {

		final IRI xt=x.getDatatype();
		final IRI yt=y.getDatatype();

		return XSD.DOUBLE.equals(xt) || XSD.DOUBLE.equals(yt) ? comparable(x, y, Literal::doubleValue)
				: XSD.FLOAT.equals(xt) || XSD.FLOAT.equals(yt) ? comparable(x, y, Literal::floatValue)
				: XSD.DECIMAL.equals(xt) || XSD.DECIMAL.equals(yt) ? comparable(x, y, Literal::decimalValue)
				: XSD.INTEGER.equals(xt) || XSD.INTEGER.equals(yt) ? comparable(x, y, Literal::integerValue)
				: comparable(x, y, Literal::longValue);
	}

	private int temporal(final Literal x, final Literal y) {

		final IRI xt=x.getDatatype();
		final IRI yt=y.getDatatype();

		return chain(compare(xt, yt), () -> comparable(x, y, v -> {

			final TemporalAccessor accessor=v.temporalAccessorValue();

			return OffsetDateTime.from(new TemporalAccessor() {

				@Override public boolean isSupported(final TemporalField field) {
					return true;
				}

				@Override public long getLong(final TemporalField field) {
					return accessor.isSupported(field) ? accessor.getLong(field) : field.range().getMinimum();
				}

			});

		}));
	}

	private int duration(final Literal x, final Literal y) {
		return comparable(x, y, v -> Duration.from(v.temporalAmountValue()));
	}

	private int plain(final Literal x, final Literal y) {
		return x.getLabel().compareTo(y.getLabel());
	}

	private int tagged(final Literal x, final Literal y) {
		return chain(x.getLanguage().orElse("").compareTo(y.getLanguage().orElse("")), () -> plain(x, y));
	}

	private int typed(final Literal x, final Literal y) {
		return chain(compare(x.getDatatype(), y.getDatatype()), () -> plain(x, y));
	}

	private > int comparable(
			final Literal x, final Literal y, final Function mapper
	) {

		final T xv=guard(() -> mapper.apply(x));
		final T yv=guard(() -> mapper.apply(y));

		return xv == null && yv == null ? typed(x, y)
				: xv == null ? 1
				: yv == null ? -1
				: xv.compareTo(yv);
	}


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	private int chain(final int order, final IntSupplier next) {
		return order != 0 ? order : next.getAsInt();
	}

	private  T guard(final Supplier supplier) {
		try {

			return supplier.get();

		} catch ( final RuntimeException e ) {

			return null;

		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy