/*
* 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.List;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;
import ch.njol.skript.Skript;
import ch.njol.skript.classes.Converter;
import ch.njol.skript.expressions.base.EventValueExpression;
import ch.njol.skript.util.Getter;
/**
* @author Peter Güttinger
*/
public class EventValues {
private EventValues() {}
private final static class EventValueInfo<E extends Event, T> {
public final Class<E> event;
public final Class<T> c;
public final Getter<T, E> getter;
@Nullable
public final Class<? extends E>[] exculdes;
@Nullable
public final String excludeErrorMessage;
public EventValueInfo(final Class<E> event, final Class<T> c, final Getter<T, E> getter, final @Nullable String excludeErrorMessage, final @Nullable Class<? extends E>[] exculdes) {
assert event != null;
assert c != null;
assert getter != null;
this.event = event;
this.c = c;
this.getter = getter;
this.exculdes = exculdes;
this.excludeErrorMessage = excludeErrorMessage;
}
}
private final static List<EventValueInfo<?, ?>> defaultEventValues = new ArrayList<EventValueInfo<?, ?>>(30);
private final static List<EventValueInfo<?, ?>> futureEventValues = new ArrayList<EventValueInfo<?, ?>>();
private final static List<EventValueInfo<?, ?>> pastEventValues = new ArrayList<EventValueInfo<?, ?>>();
private final static List<EventValueInfo<?, ?>> getEventValuesList(final int time) {
if (time == -1)
return pastEventValues;
if (time == 0)
return defaultEventValues;
if (time == 1)
return futureEventValues;
throw new IllegalArgumentException("time must be -1, 0, or 1");
}
/**
* Registers an event value.
*
* @param e the event type
* @param c the type of the default value
* @param g the getter to get the value
* @param time -1 if this is the value before the event, 1 if after, and 0 if it's the default or this value doesn't have distinct states.
* <b>Always register a default state!</b> You can leave out one of the other states instead, e.g. only register a default and a past state. The future state will
* default to the default state in this case.
*/
public static <T, E extends Event> void registerEventValue(final Class<E> e, final Class<T> c, final Getter<T, E> g, final int time) {
registerEventValue(e, c, g, time, null, (Class<? extends E>[]) null);
}
@Deprecated
public static <T, E extends Event> void registerEventValue(final Class<E> e, final Class<T> c, final ch.njol.skript.classes.SerializableGetter<T, E> g, final int time) {
registerEventValue(e, c, (Getter<T, E>) g, time);
}
/**
* Same as {@link #registerEventValue(Class, Class, Getter, int)}
*
* @param e
* @param c
* @param g
* @param time
* @param excludes Subclasses of the event for which this event value should not be registered for
*/
public static <T, E extends Event> void registerEventValue(final Class<E> e, final Class<T> c, final Getter<T, E> g, final int time, final @Nullable String excludeErrorMessage, final @Nullable Class<? extends E>... excludes) {
Skript.checkAcceptRegistrations();
final List<EventValueInfo<?, ?>> eventValues = getEventValuesList(time);
for (int i = 0; i < eventValues.size(); i++) {
final EventValueInfo<?, ?> info = eventValues.get(i);
if (info.event != e ? info.event.isAssignableFrom(e) : info.c.isAssignableFrom(c)) {
eventValues.add(i, new EventValueInfo<E, T>(e, c, g, excludeErrorMessage, excludes));
return;
}
}
eventValues.add(new EventValueInfo<E, T>(e, c, g, excludeErrorMessage, excludes));
}
@Deprecated
public static <T, E extends Event> void registerEventValue(final Class<E> e, final Class<T> c, final ch.njol.skript.classes.SerializableGetter<T, E> g, final int time, final @Nullable String excludeErrorMessage, final @Nullable Class<? extends E>... excludes) {
registerEventValue(e, c, (Getter<T, E>) g, time, excludeErrorMessage, excludes);
}
/**
* Gets a specific value from an event. Returns null if the event doesn't have such a value (conversions are done to try and get the desired value).
* <p>
* It is recommended to use {@link EventValues#getEventValueGetter(Class, Class, int)} or {@link EventValueExpression#EventValueExpression(Class)} instead of invoking this
* method repeatedly.
*
* @param e
* @param c
* @param time
* @return The event's value
* @see #registerEventValue(Class, Class, Getter, int)
*/
@Nullable
public static <T, E extends Event> T getEventValue(final E e, final Class<T> c, final int time) {
@SuppressWarnings({"null", "unchecked"})
final Getter<? extends T, ? super E> g = EventValues.getEventValueGetter((Class<E>) e.getClass(), c, time);
if (g == null)
return null;
return g.get(e);
}
/**
* Returns a getter to get a value from an event.
* <p>
* Can print an error if the event value is blocked for the given event.
*
* @param e
* @param c
* @param time
* @return A getter to get values for a given type of events
* @see #registerEventValue(Class, Class, Getter, int)
* @see EventValueExpression#EventValueExpression(Class)
*/
@Nullable
public final static <T, E extends Event> Getter<? extends T, ? super E> getEventValueGetter(final Class<E> e, final Class<T> c, final int time) {
return EventValues.getEventValueGetter(e, c, time, true);
}
@SuppressWarnings("unchecked")
@Nullable
private final static <T, E extends Event> Getter<? extends T, ? super E> getEventValueGetter(final Class<E> e, final Class<T> c, final int time, final boolean allowDefault) {
final List<EventValueInfo<?, ?>> eventValues = getEventValuesList(time);
boolean b;
for (final EventValueInfo<?, ?> ev : eventValues) {
if (((b = ev.event.isAssignableFrom(e)) || e.isAssignableFrom(ev.event)) && c.isAssignableFrom(ev.c)) {
if (!EventValues.checkExcludes(ev, e))
return null;
if (b)
return (Getter<? extends T, ? super E>) ev.getter;
return new Getter<T, E>() {
@Override
@Nullable
public T get(final E event) {
if (!ev.event.isInstance(event))
return null;
return ((Getter<? extends T, E>) ev.getter).get(event);
}
};
}
}
for (final EventValueInfo<?, ?> ev : eventValues) {
if (((b = ev.event.isAssignableFrom(e)) || e.isAssignableFrom(ev.event)) && ev.c.isAssignableFrom(c)) {
if (!EventValues.checkExcludes(ev, e))
return null;
final boolean checkInstanceOf = !b;
return new Getter<T, E>() {
@Override
@Nullable
public T get(final E event) {
if (checkInstanceOf && !e.isInstance(event))
return null;
final Object o = ((Getter<? super T, ? super E>) ev.getter).get(event);
if (c.isInstance(o))
return (T) o;
return null;
}
};
}
}
for (final EventValueInfo<?, ?> ev : eventValues) {
if ((b = ev.event.isAssignableFrom(e)) || e.isAssignableFrom(ev.event)) {
if (!EventValues.checkExcludes(ev, e))
return null;
final Getter<? extends T, ? super E> g = (Getter<? extends T, ? super E>) getConvertedGetter(ev, c, !b);
if (g != null)
return g;
}
}
if (allowDefault && time != 0)
return getEventValueGetter(e, c, 0, false);
return null;
}
private final static boolean checkExcludes(final EventValueInfo<?, ?> ev, final Class<? extends Event> e) {
final Class<? extends Event>[] excl = ev.exculdes;
if (excl == null)
return true;
for (final Class<? extends Event> ex : excl) {
if (ex.isAssignableFrom(e)) {
Skript.error(ev.excludeErrorMessage);
return false;
}
}
return true;
}
@Nullable
private final static <E extends Event, F, T> Getter<? extends T, ? super E> getConvertedGetter(final EventValueInfo<E, F> i, final Class<T> to, final boolean checkInstanceOf) {
final Converter<? super F, ? extends T> c = Converters.getConverter(i.c, to);
if (c == null)
return null;
return new Getter<T, E>() {
@Override
@Nullable
public T get(final E e) {
if (checkInstanceOf && !i.event.isInstance(e))
return null;
final F f = i.getter.get(e);
if (f == null)
return null;
return c.convert(f);
}
};
}
public final static boolean doesEventValueHaveTimeStates(final Class<? extends Event> e, final Class<?> c) {
return getEventValueGetter(e, c, -1, false) != null || getEventValueGetter(e, c, 1, false) != null;
}
}