Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/ch/njol/skript/expressions/ExprFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public boolean canReturnKeys() {

@Override
public boolean areKeysRecommended() {
return false;
return KeyProviderExpression.areKeysRecommended(unfilteredObjects);
}

@Override
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/ch/njol/skript/expressions/ExprIndices.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ public Class<? extends String> getReturnType() {
return String.class;
}

@Override
public boolean allowNestedStructures() {
return keyedExpression.allowNestedStructures();
}

@Override
public String toString(@Nullable Event e, boolean debug) {
String text = "indices of " + keyedExpression.toString(e, debug);
Expand Down
23 changes: 21 additions & 2 deletions src/main/java/ch/njol/skript/expressions/ExprKeyed.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.KeyProviderExpression;
import ch.njol.skript.lang.KeyedValue;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Iterator;

@Name("Keyed")
@Description({
"This expression is used to explicitly pass the keys of an expression alongside its values.",
Expand Down Expand Up @@ -69,12 +72,23 @@ public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean is

@Override
public @NotNull String @NotNull [] getArrayKeys(Event event) throws IllegalStateException {
return ((KeyProviderExpression<?>) getExpr()).getArrayKeys(event);
return getExpr().getArrayKeys(event);
}

@Override
public @NotNull String @NotNull [] getAllKeys(Event event) {
return ((KeyProviderExpression<?>) getExpr()).getAllKeys(event);
return getExpr().getAllKeys(event);
}

@Override
public Iterator<KeyedValue<Object>> keyedIterator(Event event) {
//noinspection unchecked,rawtypes
return (Iterator) getExpr().keyedIterator(event);
}

@Override
public boolean isIndexLoop(String input) {
return getExpr().isIndexLoop(input);
}

@Override
Expand All @@ -87,4 +101,9 @@ public String toString(@Nullable Event event, boolean debug) {
return "keyed " + getExpr().toString(event, debug);
}

@Override
public KeyProviderExpression<?> getExpr() {
return (KeyProviderExpression<?>) super.getExpr();
}

}
109 changes: 109 additions & 0 deletions src/main/java/ch/njol/skript/expressions/ExprRecursive.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package ch.njol.skript.expressions;

import ch.njol.skript.Skript;
import ch.njol.skript.SkriptAPIException;
import ch.njol.skript.doc.*;
import ch.njol.skript.expressions.base.WrapperExpression;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.KeyProviderExpression;
import ch.njol.skript.lang.KeyedValue;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Iterator;

@Name("Recursive")
@Description("Returns all values of an expression, including those in nested structures such as lists of lists.")
@Example("""
on load:
set {_data::a::b::c} to "value1"
set {_data::a::b::d} to "value2"
set {_data::a::e} to "value3"
set {_data::f} to "value4"

broadcast recursive {_data::*}
# broadcasts "value1", "value2", "value3", "value4"

broadcast recursive indices of {_data::*}
# broadcasts "a::b::c", "a::b::d", "a::e", "f"
""")
@Since("INSERT VERSION")
@Keywords({"deep", "nested"})
public class ExprRecursive extends WrapperExpression<Object> implements KeyProviderExpression<Object> {

static {
Skript.registerExpression(ExprRecursive.class, Object.class, ExpressionType.COMBINED, "recursive %~objects%");
}

private boolean returnsKeys;

@Override
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
if (!expressions[0].allowNestedStructures()) {
Skript.error(expressions[0] + " does not support nested structures.");
return false;
}
setExpr(expressions[0]);
returnsKeys = KeyProviderExpression.canReturnKeys(getExpr());
return true;
}

@Override
public Object[] getAll(Event event) {
return getExpr().getAll(event);
}

@Override
public @NotNull String @NotNull [] getArrayKeys(Event event) throws IllegalStateException {
if (!returnsKeys)
throw new IllegalStateException();
return ((KeyProviderExpression<?>) getExpr()).getArrayKeys(event);
}

@Override
public @NotNull String @NotNull [] getAllKeys(Event event) {
if (!returnsKeys)
throw new IllegalStateException();
return ((KeyProviderExpression<?>) getExpr()).getAllKeys(event);
}

@Override
public Iterator<KeyedValue<Object>> keyedIterator(Event event) {
if (!returnsKeys)
throw new IllegalStateException();
//noinspection unchecked
return ((KeyProviderExpression<Object>) getExpr()).keyedIterator(event);
}

@Override
public boolean canReturnKeys() {
return returnsKeys;
}

@Override
public boolean areKeysRecommended() {
return KeyProviderExpression.areKeysRecommended(getExpr());
}

@Override
public boolean isIndexLoop(String input) {
if (!returnsKeys)
throw new IllegalStateException();
return ((KeyProviderExpression<?>) getExpr()).isIndexLoop(input);
}

@Override
public boolean isLoopOf(String input) {
return getExpr().isLoopOf(input);
}

@Override
public String toString(@Nullable Event event, boolean debug) {
return "recursive " + getExpr().toString(event, debug);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public boolean canReturnKeys() {

@Override
public boolean areKeysRecommended() {
return false;
return KeyProviderExpression.areKeysRecommended(mappingExpr);
}

@Override
Expand Down
27 changes: 25 additions & 2 deletions src/main/java/ch/njol/skript/expressions/ExprValueWithin.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.registrations.Classes;
import ch.njol.skript.util.ClassInfoReference;
import ch.njol.skript.util.Utils;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Iterator;

@Name("Value Within")
@Description(
"Gets the value within objects. Usually used with variables to get the value they store rather than the variable itself, " +
Expand Down Expand Up @@ -81,14 +82,29 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
return ((KeyProviderExpression<?>) getExpr()).getArrayKeys(event);
}

@Override
public @NotNull String @NotNull [] getAllKeys(Event event) {
if (!returnsKeys)
throw new IllegalStateException();
return ((KeyProviderExpression<?>) getExpr()).getAllKeys(event);
}

@Override
public Iterator<KeyedValue<Object>> keyedIterator(Event event) {
if (!returnsKeys)
throw new IllegalStateException();
//noinspection unchecked
return ((KeyProviderExpression<Object>) getExpr()).keyedIterator(event);
}

@Override
public boolean canReturnKeys() {
return returnsKeys;
}

@Override
public boolean areKeysRecommended() {
return false;
return KeyProviderExpression.areKeysRecommended(getExpr());
}

@Override
Expand All @@ -107,6 +123,13 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
changer.change(getArray(event), delta, mode);
}

@Override
public boolean isIndexLoop(String input) {
if (!returnsKeys)
throw new IllegalStateException();
return ((KeyProviderExpression<?>) getExpr()).isIndexLoop(input);
}

@Override
public boolean isLoopOf(String input) {
return getExpr().isLoopOf(input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public int getTime() {
return expr.getTime();
}

@Override
public boolean allowNestedStructures() {
return expr.allowNestedStructures();
}

@Override
public boolean isDefault() {
return expr.isDefault();
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/ch/njol/skript/lang/Expression.java
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,20 @@ default boolean canReturn(Class<?> returnType) {
*/
int getTime();

/**
* Allows nested structures for this expression, i.e. lists of lists.
* <p>
* <b>Note</b>:
* Nested structures must be flattened in {@link #getArray(Event)} and {@link #getAll(Event)},
* i.e. if this expression returns a list of lists of players,
* {@link #getArray(Event)} must return a single array containing all players of all lists
*
* @return Whether this expression allows nested structures.
*/
default boolean allowNestedStructures() {
return false;
}

/**
* Returns whether this value represents the default value of its type for the event, i.e. it can be replaced with a call to event.getXyz() if one knows the event & value type.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* {@link #getAllKeys(Event)}.</li>
* <li>{@link #getArrayKeys(Event)} might be called after the corresponding {@link #getArray(Event)}</li>
* <li>{@link #getAllKeys(Event)} might be called after the corresponding {@link #getAll(Event)}</li>
* <li>{@link #isLoopOf(String)} should be overridden to return {@code KeyProviderExpression.super.isLoopOf(input) || ...}</li>
* </ul>
* <br/>
* <h2>Advice on Caching</h2>
Expand Down Expand Up @@ -163,7 +164,6 @@ default boolean isLoopOf(String input) {
return canReturnKeys() && isIndexLoop(input);
}


/**
* Checks whether the 'loop-...' expression should match this loop's index,
* e.g. loop-index matches the index of a loop that iterates over a list variable.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
*/
public interface KeyReceiverExpression<T> extends Expression<T> {

/**
* Returns whether this expression's changer supports nested structures.
*
* @return true if nested structures are supported, false otherwise
*/
default boolean acceptsNestedStructures() {
return false;
}

/**
* An alternative changer method that provides a set of keys as well as a set of values.
* This is only ever called for {@link ChangeMode#supportsKeyedChange()} safe change modes,
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/ch/njol/skript/lang/KeyedValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.function.Function;

/**
* A record that represents a key-value pair
Expand Down Expand Up @@ -56,6 +57,34 @@ public <U> KeyedValue<U> withValue(@NotNull U newValue) {
return new KeyedValue<>(key(), newValue);
}

/**
* Maps an array of {@link KeyedValue} objects to a new array by applying a mapping function to each value.
* <p>
* For each non-null element in the source array, the provided mapper function is applied to its value.
* If the result of the mapping is non-null, a new {@link KeyedValue} is created with the same key and the mapped value.
* If the mapping result is null, the corresponding element in the result array will be null.
* <p>
* Null elements in the source array are skipped and remain null in the result array.
*
* @param source the source array of {@link KeyedValue} objects to map; may be null
* @param mapper a function to apply to each value in the source array
* @return a new array of {@link KeyedValue} objects with mapped values; never null, but may contain null elements
*/
public static <T, U> KeyedValue<U>[] map(KeyedValue<T>[] source, Function<T, @Nullable U> mapper) {
if (source == null)
//noinspection unchecked
return new KeyedValue[0];
//noinspection unchecked
KeyedValue<U>[] mapped = new KeyedValue[source.length];
for (int i = 0; i < source.length; i++) {
if (source[i] == null)
continue;
U mappedValue = mapper.apply(source[i].value());
mapped[i] = mappedValue != null ? source[i].withValue(mappedValue) : null;
}
return mapped;
}

/**
* Zips the given values and keys into a {@link KeyedValue} array.
*
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/ch/njol/skript/lang/SkriptParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,10 @@ record SignatureData(ClassInfo<?> classInfo, boolean plural) { }
}
}

for (Expression<?> param : params) {
if (KeyProviderExpression.areKeysRecommended(param))
param.allowNestedStructures();
}
FunctionReference<T> functionReference = new FunctionReference<>(functionName, SkriptLogger.getNode(), namespace, types, params);
if (!functionReference.validateFunction(true)) {
log.printError();
Expand Down
Loading