/*
* 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.lang.util;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;
import ch.njol.skript.classes.Changer;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.classes.Converter;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.registrations.Classes;
import ch.njol.skript.registrations.Converters;
import ch.njol.util.Checker;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
/**
* Represents a expression converted to another type. This, and not Expression, is the required return type of {@link SimpleExpression#getConvertedExpr(Class...)} because this
* class
* <ol>
* <li>automatically lets the source expression handle everything apart from the get() methods</li>
* <li>will never convert itself to another type, but rather request a new converted expression from the source expression.</li>
* </ol>
*
* @author Peter Güttinger
*/
public class ConvertedExpression<F, T> implements Expression<T> {
protected Expression<? extends F> source;
protected Class<T> to;
final Converter<? super F, ? extends T> conv;
public ConvertedExpression(final Expression<? extends F> source, final Class<T> to, final Converter<? super F, ? extends T> conv) {
assert source != null;
assert to != null;
assert conv != null;
this.source = source;
this.to = to;
this.conv = conv;
}
@Nullable
public static <F, T> ConvertedExpression<F, T> newInstance(final Expression<F> v, final Class<T>... to) {
assert !CollectionUtils.containsSuperclass(to, v.getReturnType());
for (final Class<T> c : to) { // REMIND try more converters? -> also change WrapperExpression (and maybe ExprLoopValue)
assert c != null;
// casting <? super ? extends F> to <? super F> is wrong, but since the converter is only used for values returned by the expression
// (which are instances of "<? extends F>") this won't result in any ClassCastExceptions.
@SuppressWarnings("unchecked")
final Converter<? super F, ? extends T> conv = (Converter<? super F, ? extends T>) Converters.getConverter(v.getReturnType(), c);
if (conv == null)
continue;
return new ConvertedExpression<F, T>(v, c, conv);
}
return null;
}
@Override
public final boolean init(final Expression<?>[] vars, final int matchedPattern, final Kleenean isDelayed, final ParseResult matcher) {
throw new UnsupportedOperationException();
}
@Override
public String toString(final @Nullable Event e, final boolean debug) {
if (debug && e == null)
return "(" + source.toString(e, debug) + " >> " + conv + ": " + source.getReturnType().getName() + "->" + to.getName() + ")";
return source.toString(e, debug);
}
@Override
public String toString() {
return toString(null, false);
}
@Override
public Class<T> getReturnType() {
return to;
}
@Override
public boolean isSingle() {
return source.isSingle();
}
@SuppressWarnings("unchecked")
@Override
@Nullable
public <R> Expression<? extends R> getConvertedExpression(final Class<R>... to) {
if (CollectionUtils.containsSuperclass(to, this.to))
return (Expression<? extends R>) this;
return source.getConvertedExpression(to);
}
@Nullable
private ClassInfo<? super T> returnTypeInfo;
@Override
@Nullable
public Class<?>[] acceptChange(final ChangeMode mode) {
final Class<?>[] r = source.acceptChange(mode);
if (r == null) {
ClassInfo<? super T> rti = returnTypeInfo;
returnTypeInfo = rti = Classes.getSuperClassInfo(getReturnType());
final Changer<?> c = rti.getChanger();
return c == null ? null : c.acceptChange(mode);
}
return r;
}
@Override
public void change(final Event e, final @Nullable Object[] delta, final ChangeMode mode) {
final ClassInfo<? super T> rti = returnTypeInfo;
if (rti != null) {
final Changer<? super T> c = rti.getChanger();
if (c != null)
c.change(getArray(e), delta, mode);
} else {
source.change(e, delta, mode);
}
}
@Override
@Nullable
public T getSingle(final Event e) {
final F f = source.getSingle(e);
if (f == null)
return null;
return conv.convert(f);
}
@Override
public T[] getArray(final Event e) {
return Converters.convert(source.getArray(e), to, conv);
}
@Override
public T[] getAll(final Event e) {
return Converters.convert(source.getAll(e), to, conv);
}
@Override
public boolean check(final Event e, final Checker<? super T> c, final boolean negated) {
return negated ^ check(e, c);
}
@Override
public boolean check(final Event e, final Checker<? super T> c) {
return source.check(e, new Checker<F>() {
@Override
public boolean check(final F f) {
final T t = conv.convert(f);
if (t == null)
return false;
return c.check(t);
}
});
}
@Override
public boolean getAnd() {
return source.getAnd();
}
@Override
public boolean setTime(final int time) {
return source.setTime(time);
}
@Override
public int getTime() {
return source.getTime();
}
@Override
public boolean isDefault() {
return source.isDefault();
}
@Override
public boolean isLoopOf(final String s) {
return false;// A loop does not convert the expression to loop
}
@Override
@Nullable
public Iterator<T> iterator(final Event e) {
final Iterator<? extends F> iter = source.iterator(e);
if (iter == null)
return null;
return new Iterator<T>() {
@Nullable
T next = null;
@Override
public boolean hasNext() {
if (next != null)
return true;
while (next == null && iter.hasNext()) {
final F f = iter.next();
next = f == null ? null : conv.convert(f);
}
return next != null;
}
@Override
public T next() {
if (!hasNext())
throw new NoSuchElementException();
final T n = next;
next = null;
assert n != null;
return n;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public Expression<?> getSource() {
return source;
}
@SuppressWarnings("unchecked")
@Override
public Expression<? extends T> simplify() {
final Expression<? extends T> c = source.simplify().getConvertedExpression(to);
if (c != null)
return c;
return this;
}
}