/*
* 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;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
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.ChangeMode;
import ch.njol.skript.conditions.CondCompare;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.util.SimpleLiteral;
import ch.njol.skript.util.Utils;
import ch.njol.util.Checker;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
/**
* A list of expressions.
*
* @author Peter Güttinger
*/
public class ExpressionList<T> implements Expression<T> {
protected final Expression<? extends T>[] expressions;
protected boolean and;
private final boolean single;
private final Class<T> returnType;
@Nullable
private ExpressionList<?> source;
public ExpressionList(final Expression<? extends T>[] expressions, final Class<T> returnType, final boolean and) {
this(expressions, returnType, and, null);
}
protected ExpressionList(final Expression<? extends T>[] expressions, final Class<T> returnType, final boolean and, final @Nullable ExpressionList<?> source) {
assert expressions != null && expressions.length > 1;
this.expressions = expressions;
this.returnType = returnType;
this.and = and;
if (and) {
single = false;
} else {
boolean single = true;
for (final Expression<?> e : expressions) {
if (!e.isSingle()) {
single = false;
break;
}
}
this.single = single;
}
this.source = source;
}
@Override
public boolean init(final Expression<?>[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) {
throw new UnsupportedOperationException();
}
@Override
@Nullable
public T getSingle(final Event e) {
if (!single)
throw new UnsupportedOperationException();
for (final int i : CollectionUtils.permutation(expressions.length)) {
final T t = expressions[i].getSingle(e);
if (t != null)
return t;
}
return null;
}
@Override
public T[] getArray(final Event e) {
if (and)
return getAll(e);
for (final int i : CollectionUtils.permutation(expressions.length)) {
final T[] t = expressions[i].getArray(e);
if (t.length > 0)
return t;
}
@SuppressWarnings("unchecked")
final T[] r = (T[]) Array.newInstance(returnType, 0);
assert r != null;
return r;
}
@SuppressWarnings({"null", "unchecked"})
@Override
public T[] getAll(final Event e) {
final ArrayList<T> r = new ArrayList<T>();
for (final Expression<? extends T> expr : expressions)
r.addAll(Arrays.asList(expr.getAll(e)));
return r.toArray((T[]) Array.newInstance(returnType, r.size()));
}
@Override
@Nullable
public Iterator<? extends T> iterator(final Event e) {
if (!and) {
for (final int i : CollectionUtils.permutation(expressions.length)) {
final Iterator<? extends T> t = expressions[i].iterator(e);
if (t != null && t.hasNext())
return t;
}
return null;
}
return new Iterator<T>() {
private int i = 0;
@Nullable
private Iterator<? extends T> current = null;
@Override
public boolean hasNext() {
Iterator<? extends T> c = current;
while (i < expressions.length && (c == null || !c.hasNext()))
current = c = expressions[i++].iterator(e);
return c != null && c.hasNext();
}
@Override
public T next() {
if (!hasNext())
throw new NoSuchElementException();
final Iterator<? extends T> c = current;
if (c == null)
throw new NoSuchElementException();
final T t = c.next();
assert t != null : current;
return t;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public boolean isSingle() {
return single;
}
@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) {
for (final Expression<? extends T> expr : expressions) {
final Boolean b = expr.check(e, c);
if (and && !b)
return false;
if (!and && b)
return true;
}
return and;
}
@SuppressWarnings("unchecked")
@Override
@Nullable
public <R> Expression<? extends R> getConvertedExpression(final Class<R>... to) {
final Expression<? extends R>[] exprs = new Expression[expressions.length];
for (int i = 0; i < exprs.length; i++)
if ((exprs[i] = expressions[i].getConvertedExpression(to)) == null)
return null;
return new ExpressionList<R>(exprs, (Class<R>) Utils.getSuperType(to), and, this);
}
@Override
public Class<T> getReturnType() {
return returnType;
}
@Override
public boolean getAnd() {
return and;
}
/**
* For use in {@link CondCompare} only.
*
* @return The old 'and' value
*/
public boolean setAnd(final boolean and) {
final boolean r = and;
this.and = and;
return r;
}
/**
* For use in {@link CondCompare} only.
*/
public void invertAnd() {
and = !and;
}
@Override
@Nullable
public Class<?>[] acceptChange(final ChangeMode mode) {
Class<?>[] l = expressions[0].acceptChange(mode);
if (l == null)
return null;
final ArrayList<Class<?>> r = new ArrayList<Class<?>>(Arrays.asList(l));
for (int i = 1; i < expressions.length; i++) {
l = expressions[i].acceptChange(mode);
if (l == null)
return null;
r.retainAll(Arrays.asList(l));
if (r.isEmpty())
return null;
}
return r.toArray(new Class[r.size()]);
}
@Override
public void change(final Event e, final @Nullable Object[] delta, final ChangeMode mode) throws UnsupportedOperationException {
for (final Expression<?> expr : expressions) {
expr.change(e, delta, mode);
}
}
private int time = 0;
@Override
public boolean setTime(final int time) {
boolean ok = false;
for (final Expression<?> e : expressions) {
ok |= e.setTime(time);
}
if (ok)
this.time = time;
return ok;
}
@Override
public int getTime() {
return time;
}
@Override
public boolean isDefault() {
return false;
}
@Override
public boolean isLoopOf(final String s) {
for (final Expression<?> e : expressions)
if (e.isLoopOf(s))
return true;
return false;
}
@Override
public Expression<?> getSource() {
final ExpressionList<?> s = source;
return s == null ? this : s;
}
@Override
public String toString(final @Nullable Event e, final boolean debug) {
final StringBuilder b = new StringBuilder("(");
for (int i = 0; i < expressions.length; i++) {
if (i != 0) {
if (i == expressions.length - 1)
b.append(and ? " and " : " or ");
else
b.append(", ");
}
b.append(expressions[i].toString(e, debug));
}
b.append(")");
if (debug)
b.append("[").append(returnType).append("]");
return "" + b;
}
@Override
public String toString() {
return toString(null, false);
}
/**
* @return The internal list of expressions. Can be modified with care.
*/
public Expression<? extends T>[] getExpressions() {
return expressions;
}
@Override
public Expression<T> simplify() {
boolean isLiteralList = true;
boolean isSimpleList = true;
for (int i = 0; i < expressions.length; i++) {
expressions[i] = expressions[i].simplify();
isLiteralList &= expressions[i] instanceof Literal;
isSimpleList &= expressions[i].isSingle();
}
if (isLiteralList && isSimpleList) {
@SuppressWarnings("unchecked")
final T[] values = (T[]) Array.newInstance(returnType, expressions.length);
for (int i = 0; i < values.length; i++)
values[i] = ((Literal<? extends T>) expressions[i]).getSingle();
return new SimpleLiteral<T>(values, returnType, and);
}
if (isLiteralList) {
final Literal<? extends T>[] ls = Arrays.copyOf(expressions, expressions.length, Literal[].class);
assert ls != null;
return new LiteralList<T>(ls, returnType, and);
}
return this;
}
}