/** * This file is part of Skript. * * Skript 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 3 of the License, or * (at your option) any later version. * * Skript 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 Skript. If not, see <http://www.gnu.org/licenses/>. * * * Copyright 2011-2017 Peter Güttinger and contributors */ package ch.njol.skript.registrations; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.Skript; import ch.njol.skript.classes.Comparator; import ch.njol.skript.classes.Comparator.ComparatorInfo; import ch.njol.skript.classes.Comparator.Relation; import ch.njol.skript.classes.Converter; import ch.njol.skript.classes.InverseComparator; import ch.njol.util.Pair; /** * @author Peter Güttinger */ public class Comparators { private Comparators() {} public final static Collection<ComparatorInfo<?, ?>> comparators = new ArrayList<ComparatorInfo<?, ?>>(); /** * Registers a {@link Comparator}. * * @param t1 * @param t2 * @param c * @throws IllegalArgumentException if any given class is equal to <code>Object.class</code> */ public static <T1, T2> void registerComparator(final Class<T1> t1, final Class<T2> t2, final Comparator<T1, T2> c) { Skript.checkAcceptRegistrations(); if (t1 == Object.class && t2 == Object.class) throw new IllegalArgumentException("You must not add a comparator for Objects"); comparators.add(new ComparatorInfo<T1, T2>(t1, t2, c)); } @SuppressWarnings({"rawtypes", "unchecked"}) public static Relation compare(final @Nullable Object o1, final @Nullable Object o2) { if (o1 == null || o2 == null) return Relation.NOT_EQUAL; final Comparator c = getComparator(o1.getClass(), o2.getClass()); if (c == null) return Relation.NOT_EQUAL; return c.compare(o1, o2); } private final static java.util.Comparator<Object> javaComparator = new java.util.Comparator<Object>() { @Override public int compare(final @Nullable Object o1, final @Nullable Object o2) { return Comparators.compare(o1, o2).getRelation(); } }; public static java.util.Comparator<Object> getJavaComparator() { return javaComparator; } private final static Map<Pair<Class<?>, Class<?>>, Comparator<?, ?>> comparatorsQuickAccess = new HashMap<Pair<Class<?>, Class<?>>, Comparator<?, ?>>(); @SuppressWarnings("unchecked") @Nullable public static <F, S> Comparator<? super F, ? super S> getComparator(final Class<F> f, final Class<S> s) { final Pair<Class<?>, Class<?>> p = new Pair<Class<?>, Class<?>>(f, s); if (comparatorsQuickAccess.containsKey(p)) return (Comparator<? super F, ? super S>) comparatorsQuickAccess.get(p); final Comparator<?, ?> comp = getComparator_i(f, s); comparatorsQuickAccess.put(p, comp); return (Comparator<? super F, ? super S>) comp; } @SuppressWarnings("unchecked") @Nullable private static <F, S> Comparator<?, ?> getComparator_i(final Class<F> f, final Class<S> s) { // perfect match for (final ComparatorInfo<?, ?> info : comparators) { if (info.c1.isAssignableFrom(f) && info.c2.isAssignableFrom(s)) { return info.c; } else if (info.c1.isAssignableFrom(s) && info.c2.isAssignableFrom(f)) { return new InverseComparator<F, S>((Comparator<? super S, ? super F>) info.c); } } // same class but no comparator if (s == f && f != Object.class && s != Object.class) { return Comparator.equalsComparator; } final boolean[] trueFalse = {true, false}; Converter<? super F, ?> c1; Converter<? super S, ?> c2; // single conversion for (final ComparatorInfo<?, ?> info : comparators) { for (final boolean first : trueFalse) { if (info.getType(first).isAssignableFrom(f)) { c2 = Converters.getConverter(s, info.getType(!first)); if (c2 != null) { return first ? new ConvertedComparator<F, S>(info.c, c2) : new InverseComparator<F, S>(new ConvertedComparator<S, F>(c2, info.c)); } } if (info.getType(first).isAssignableFrom(s)) { c1 = Converters.getConverter(f, info.getType(!first)); if (c1 != null) { return !first ? new ConvertedComparator<F, S>(c1, info.c) : new InverseComparator<F, S>(new ConvertedComparator<S, F>(info.c, c1)); } } } } // double conversion for (final ComparatorInfo<?, ?> info : comparators) { for (final boolean first : trueFalse) { c1 = Converters.getConverter(f, info.getType(first)); c2 = Converters.getConverter(s, info.getType(!first)); if (c1 != null && c2 != null) { return first ? new ConvertedComparator<F, S>(c1, info.c, c2) : new InverseComparator<F, S>(new ConvertedComparator<S, F>(c2, info.c, c1)); } } } return null; } private final static class ConvertedComparator<T1, T2> implements Comparator<T1, T2> { @SuppressWarnings("rawtypes") private final Comparator c; @SuppressWarnings("rawtypes") @Nullable private final Converter c1, c2; public ConvertedComparator(final Converter<? super T1, ?> c1, final Comparator<?, ?> c) { this.c1 = c1; this.c = c; this.c2 = null; } public ConvertedComparator(final Comparator<?, ?> c, final Converter<? super T2, ?> c2) { this.c1 = null; this.c = c; this.c2 = c2; } public ConvertedComparator(final Converter<? super T1, ?> c1, final Comparator<?, ?> c, final Converter<? super T2, ?> c2) { this.c1 = c1; this.c = c; this.c2 = c2; } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public Relation compare(final T1 o1, final T2 o2) { final Converter c1 = this.c1; final Object t1 = c1 == null ? o1 : c1.convert(o1); if (t1 == null) return Relation.NOT_EQUAL; final Converter c2 = this.c2; final Object t2 = c2 == null ? o2 : c2.convert(o2); if (t2 == null) return Relation.NOT_EQUAL; return c.compare(t1, t2); } @Override public boolean supportsOrdering() { return c.supportsOrdering(); } @Override public String toString() { return "ConvertedComparator(" + c1 + "," + c + "," + c2 + ")"; } } }