/*
* 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-2014 Peter Güttinger
*
*/
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 final static Relation compare(final @Nullable Object o1, final @Nullable Object o2) {
if (o1 == null || o2 == null)
return Relation.NOT_EQUAL;
@SuppressWarnings("null")
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 Object o1, final Object o2) {
return Comparators.compare(o1, o2).getRelation();
}
};
public final 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 final 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 final 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 + ")";
}
}
}