/*
* 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.expressions;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;
import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.lang.util.SimpleLiteral;
import ch.njol.util.Kleenean;
/**
* @author Peter Güttinger
*/
@Name("Numbers")
@Description({"All numbers between two given numbers, useful for looping.",
"Use 'numbers' if your start is not an integer and you want to keep the fractional part of the start number constant, or use 'integers' if you only want to loop integers.",
"An integer loop from 1 to a number x can also be written as 'loop x times'."})
@Examples({"loop 5 times: # loops 1, 2, 3, 4, 5",
"loop numbers from 2.5 to 5.5: # loops 2.5, 3.5, 4.5, 5.5",
"loop integers from 2.9 to 5.1: # same as '3 to 5', i.e. loops 3, 4, 5"})
@Since("1.4.6")
public class ExprNumbers extends SimpleExpression<Number> {
static {
Skript.registerExpression(ExprNumbers.class, Number.class, ExpressionType.COMBINED,
"[(all|the)] (numbers|1¦integers) (between|from) %number% (and|to) %number%",
"%number% times");
}
@SuppressWarnings("null")
private Expression<Number> start, end;
boolean integer;
@SuppressWarnings({"unchecked", "null"})
@Override
public boolean init(final Expression<?>[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) {
start = matchedPattern == 0 ? (Expression<Number>) exprs[0] : new SimpleLiteral<Number>(1, false);
end = (Expression<Number>) exprs[1 - matchedPattern];
integer = parseResult.mark == 1 || matchedPattern == 1;
return true;
}
@Override
@Nullable
protected Number[] get(final Event e) {
final Number s = start.getSingle(e), f = end.getSingle(e);
if (s == null || f == null || s.doubleValue() > f.doubleValue())
return null;
final Number[] array = integer ? new Long[(int) (Math.floor(f.doubleValue()) - Math.ceil(s.doubleValue()) + 1)] : new Double[(int) Math.floor(f.doubleValue() - s.doubleValue() + 1)];
final double low = integer ? Math.ceil(s.doubleValue()) : s.doubleValue();
for (int i = 0; i < array.length; i++) {
if (integer)
array[i] = Long.valueOf((long) low + i);
else
array[i] = Double.valueOf(low + i);
}
return array;
}
@Override
@Nullable
public Iterator<Number> iterator(final Event e) {
final Number s = start.getSingle(e), f = end.getSingle(e);
if (s == null || f == null || s.doubleValue() > f.doubleValue())
return null;
return new Iterator<Number>() {
double i = integer ? Math.ceil(s.doubleValue()) : s.doubleValue(), max = integer ? Math.floor(f.doubleValue()) : f.doubleValue();
@Override
public boolean hasNext() {
return i <= max;
}
@SuppressWarnings("null")
@Override
public Number next() {
if (!hasNext())
throw new NoSuchElementException();
if (integer)
return Long.valueOf((long) i++);
else
return Double.valueOf(i++);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public String toString(final @Nullable Event e, final boolean debug) {
return (integer ? "integers" : "numbers") + " from " + start.toString(e, debug) + " to " + end.toString(e, debug);
}
@Override
public boolean isLoopOf(final String s) {
return integer && (s.equalsIgnoreCase("integer") || s.equalsIgnoreCase("int"));
}
@Override
public boolean isSingle() {
return false;
}
@Override
public Class<? extends Number> getReturnType() {
return integer ? Long.class : Double.class;
}
}