package php.runtime.ext.core.classes.lib;
import php.runtime.Memory;
import php.runtime.common.HintType;
import php.runtime.env.Environment;
import php.runtime.invoke.Invoker;
import php.runtime.lang.BaseObject;
import php.runtime.lang.ForeachIterator;
import php.runtime.lang.spl.Countable;
import php.runtime.memory.ArrayMemory;
import php.runtime.memory.KeyValueMemory;
import php.runtime.memory.LongMemory;
import php.runtime.memory.ObjectMemory;
import php.runtime.reflection.ClassEntity;
import java.util.*;
import static php.runtime.annotation.Reflection.*;
import static php.runtime.annotation.Reflection.Optional;
import static php.runtime.annotation.Runtime.FastMethod;
@Name("php\\lib\\Arr")
public class ItemsUtils extends BaseObject {
public ItemsUtils(Environment env, ClassEntity clazz) {
super(env, clazz);
}
@Signature
private Memory __construct(Environment env, Memory... args) { return Memory.NULL; }
protected static Memory call(ForeachIterator iterator, Invoker invoker) {
if (invoker == null)
return iterator.getValue();
if (invoker.getArgumentCount() == 1)
return invoker.callNoThrow(iterator.getValue());
else
return invoker.callNoThrow(iterator.getValue(), iterator.getMemoryKey());
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE),
@Arg(value = "comparator", type = HintType.CALLABLE, optional = @Optional("null")),
@Arg(value = "saveKeys", optional = @Optional("false"))
})
public static Memory sortByKeys(Environment env, Memory... args) {
boolean saveKeys = args[2].toBoolean();
List<KeyValueMemory> tmp = new ArrayList<KeyValueMemory>();
ForeachIterator iterator = args[0].toImmutable().getNewIterator(env);
while (iterator.next()) {
tmp.add(new KeyValueMemory(iterator.getMemoryKey(), iterator.getValue().toImmutable()));
}
final Invoker invoker = args[0].isNull() ? null : Invoker.valueOf(env, null, args[1]);
Collections.sort(tmp, new Comparator<KeyValueMemory>() {
@Override
public int compare(KeyValueMemory o1, KeyValueMemory o2) {
if (invoker == null)
return o1.key.compareTo(o2.key);
else
return invoker.callNoThrow(o1.key, o2.key).toInteger();
}
});
ArrayMemory r = new ArrayMemory();
Iterator<KeyValueMemory> iterator1 = tmp.iterator();
while (iterator1.hasNext()) {
if (saveKeys)
r.add(iterator1.next());
else
r.add(iterator1.next().value);
iterator1.remove();
}
return r.toConstant();
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE),
@Arg(value = "comparator", type = HintType.CALLABLE, optional = @Optional("null")),
@Arg(value = "saveKeys", optional = @Optional("false"))
})
public static Memory sort(Environment env, Memory... args) {
boolean saveKeys = args[2].toBoolean();
Memory[] sortTmp;
if (!saveKeys && args[0].isArray()) {
Memory[] original = args[0].toValue(ArrayMemory.class).values(true);
sortTmp = Arrays.copyOf(original, original.length);
} else {
ForeachIterator iterator = args[0].toImmutable().getNewIterator(env);
List<Memory> tmp = new ArrayList<Memory>();
while (iterator.next()) {
if (saveKeys)
tmp.add(new KeyValueMemory(iterator.getMemoryKey(), iterator.getValue().toImmutable()));
else
tmp.add(iterator.getValue().toImmutable());
}
sortTmp = tmp.toArray(new Memory[tmp.size()]);
tmp.clear();
}
if (args[1].isNull()) {
Arrays.sort(sortTmp, new Comparator<Memory>() {
@Override
public int compare(Memory o1, Memory o2) {
return o1.compareTo(o2);
}
});
} else {
final Invoker invoker = Invoker.valueOf(env, null, args[1]);
Arrays.sort(sortTmp, new Comparator<Memory>() {
@Override
public int compare(Memory o1, Memory o2) {
return invoker.callNoThrow(o1, o2).toInteger();
}
});
}
ArrayMemory r = new ArrayMemory();
for(Memory el : sortTmp) {
r.add(el);
}
return r.toConstant();
}
@Signature(@Arg(value = "collection", type = HintType.TRAVERSABLE))
public static Memory count(Environment env, Memory... args) {
if (args[0].isArray())
return LongMemory.valueOf(args[0].toValue(ArrayMemory.class).size());
else if (args[0].isObject()) {
ObjectMemory objectMemory = args[0].toValue(ObjectMemory.class);
if (objectMemory.value instanceof Countable) {
env.pushCall(objectMemory.value, "count");
try {
long size = ((Countable) objectMemory.value).count(env).toLong();
return LongMemory.valueOf(size);
} finally {
env.popCall();
}
} else {
ForeachIterator iterator = args[0].getNewIterator(env);
if (iterator == null) {
return Memory.FALSE;
}
int r = 0;
while (iterator.next()) r++;
return LongMemory.valueOf(r);
}
} else
return Memory.CONST_INT_0;
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE),
@Arg(value = "withKeys", optional = @Optional("false"))
})
public static Memory toArray(Environment env, Memory... args) {
boolean withKeys = args[1].toBoolean();
if (withKeys && args[0].isArray())
return args[0].toImmutable();
ForeachIterator iterator = args[0].getNewIterator(env);
if (iterator == null) {
return Memory.NULL;
}
ArrayMemory r = new ArrayMemory();
while (iterator.next()) {
if (withKeys)
r.put(iterator.getMemoryKey(), iterator.getValue().toImmutable());
else
r.add(iterator.getValue().toImmutable());
}
return r.toConstant();
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE),
@Arg(value = "withKeys", optional = @Optional("false"))
})
public static Memory of(Environment env, Memory... args) {
return toArray(env, args);
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE)
})
public static Memory values(Environment env, Memory... args) {
return toArray(env, args[0], Memory.FALSE);
}
@Signature({
@Arg(value = "keys", type = HintType.TRAVERSABLE),
@Arg(value = "values", type = HintType.TRAVERSABLE),
})
public static Memory combine(Environment env, Memory... args) {
ForeachIterator keyIterator = args[0].getNewIterator(env);
ForeachIterator valueIterator = args[1].getNewIterator(env);
ArrayMemory r = new ArrayMemory();
while (keyIterator.next()) {
if (valueIterator.next()) {
r.refOfIndex(keyIterator.getValue()).assign(valueIterator.getValue().toImmutable());
} else {
return Memory.NULL;
}
}
return r.toConstant();
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE),
@Arg(value = "value"),
@Arg(value = "strict", optional = @Optional("false"))
})
public static Memory has(Environment env, Memory... args) {
ForeachIterator iterator = args[0].getNewIterator(env);
if (iterator == null) {
return Memory.NULL;
}
Memory needle = args[1];
boolean strict = args[2].toBoolean();
while (iterator.next()) {
if (strict) {
if (needle.identical(iterator.getValue()))
return Memory.TRUE;
} else {
if (needle.equal(iterator.getValue()))
return Memory.TRUE;
}
}
return Memory.FALSE;
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE),
@Arg(value = "callback", type = HintType.CALLABLE),
})
public static Memory map(Environment env, Memory... args) throws Throwable {
ForeachIterator iterator = args[0].getNewIterator(env);
if (iterator == null) {
return Memory.NULL;
}
Invoker callback = Invoker.valueOf(env, null, args[1]);
if (callback == null) {
return Memory.NULL;
}
ArrayMemory r = new ArrayMemory();
while (iterator.next()) {
r.refOfIndex(iterator.getMemoryKey()).assign(callback.call(iterator.getValue()));
}
return r.toConstant();
}
@Signature(@Arg("collection"))
public static Memory toList(Environment env, Memory... args) {
ArrayMemory r = new ArrayMemory();
for(Memory arg : args) {
if (arg.isTraversable()) {
ForeachIterator iterator = arg.getNewIterator(env);
while (iterator.next())
r.add(iterator.getValue().toImmutable());
} else
r.add(arg.toImmutable());
}
return r.toConstant();
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE)
})
public static Memory keys(Environment env, Memory... args) {
ForeachIterator iterator = args[0].getNewIterator(env);
if (iterator == null) {
return Memory.NULL;
}
ArrayMemory r = new ArrayMemory();
while (iterator.next())
r.add(iterator.getMemoryKey());
return r.toConstant();
}
protected static void flatten(Environment env, ForeachIterator iterator, Set<Integer> used, ArrayMemory array,
int level, int maxLevel) {
while (iterator.next()) {
Memory el = iterator.getValue();
ForeachIterator innerIterator = el.getNewIterator(env);
if (innerIterator == null || (level >= maxLevel && maxLevel > -1)) {
array.add(el.toImmutable());
} else {
if (used.add(el.getPointer())) {
flatten(env, innerIterator, used, array, level + 1, maxLevel);
used.remove(el.getPointer());
}
}
}
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE),
@Arg(value = "level", optional = @Optional("-1"))
})
public static Memory flatten(Environment env, Memory... args) {
ArrayMemory r = new ArrayMemory();
int level = args[1].toInteger();
ForeachIterator iterator = args[0].getNewIterator(env);
if (iterator == null) {
return Memory.NULL;
}
Set<Integer> used = new HashSet<Integer>();
used.add(args[0].getPointer());
flatten(env, iterator, used, r, 0, level);
return r.toConstant();
}
@Signature({
@Arg(value = "array", type = HintType.ARRAY, reference = true),
@Arg(value = "values", type = HintType.VARARG)
})
public static Memory unshift(Environment env, Memory... args) {
args[0].toValue(ArrayMemory.class).unshift(Arrays.copyOfRange(args, 1, args.length));
return Memory.NULL;
}
@Signature({
@Arg(value = "array", type = HintType.ARRAY, reference = true)
})
public static Memory shift(Environment env, Memory... args) {
return args[0].toValue(ArrayMemory.class).shift();
}
@Signature(@Arg(value = "array", type = HintType.ARRAY, reference = true))
public static Memory pop(Environment env, Memory... args) throws Throwable {
Memory array = args[0];
Memory pop = array.toValue(ArrayMemory.class).pop();
return pop == null ? Memory.NULL : pop;
}
@Signature(@Arg(value = "array", type = HintType.ARRAY))
public static Memory peak(Environment env, Memory... args) throws Throwable {
Memory array = args[0];
Memory peek = array.toValue(ArrayMemory.class).peek();
return peek == null ? Memory.NULL : peek;
}
@Signature(@Arg(value = "array", type = HintType.ARRAY))
public static Memory last(Environment env, Memory... args) throws Throwable {
return peak(env, args);
}
@Signature(@Arg(value = "array", type = HintType.ARRAY))
public static Memory lastKey(Environment env, Memory... args) throws Throwable {
Memory array = args[0];
Memory peek = array.toValue(ArrayMemory.class).peekKey();
return peek == null ? Memory.NULL : peek;
}
@Signature({
@Arg(value = "array", type = HintType.ARRAY, reference = true),
@Arg(value = "values", type = HintType.VARARG)
})
public static Memory push(Environment env, Memory... args) throws Throwable {
Memory array = args[0];
for (int i = 1; i < args.length; i++) {
array.toValue(ArrayMemory.class).add(args[i].toImmutable());
}
return Memory.NULL;
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE)
})
public static Memory first(Environment env, Memory... args) {
ForeachIterator iterator = args[0].getNewIterator(env);
if (iterator.next()) {
return iterator.getValue();
}
return Memory.NULL;
}
@Signature({
@Arg(value = "collection", type = HintType.TRAVERSABLE)
})
public static Memory firstKey(Environment env, Memory... args) {
ForeachIterator iterator = args[0].getNewIterator(env);
if (iterator.next()) {
return iterator.getMemoryKey();
}
return Memory.NULL;
}
@Signature({
@Arg(value = "collection", type = HintType.ARRAY)
})
public static Memory reverse(Environment env, Memory... args) {
ForeachIterator iterator = args[0].getNewIterator(env);
ArrayMemory result = new ArrayMemory();
while (iterator.next()) {
result.unshift(iterator.getValue().toImmutable());
}
return result.toConstant();
}
}