From 486a153a4682e16950e37d226439ff2da51c33cb Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Mon, 27 Oct 2025 19:22:27 -0400 Subject: [PATCH 1/6] Initial Commit --- .../njol/skript/expressions/ExprElement.java | 6 +- .../common/properties/PropertiesModule.java | 6 - .../expressions/PropExprElements.java | 299 ++++++++++++++++++ .../skript/common/types/QueueClassInfo.java | 156 ++++++++- .../skript/lang/properties/Property.java | 67 ++++ .../lang/properties/PropertyHandler.java | 48 +++ 6 files changed, 571 insertions(+), 11 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java diff --git a/src/main/java/ch/njol/skript/expressions/ExprElement.java b/src/main/java/ch/njol/skript/expressions/ExprElement.java index 67d354768a3..5e1d1041ce8 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprElement.java +++ b/src/main/java/ch/njol/skript/expressions/ExprElement.java @@ -1,14 +1,13 @@ package ch.njol.skript.expressions; -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.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.simplification.SimplifiedLiteral; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.registrations.Feature; import ch.njol.skript.util.LiteralUtils; @@ -20,7 +19,6 @@ import org.apache.commons.lang.ArrayUtils; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; -import ch.njol.skript.lang.simplification.SimplifiedLiteral; import org.skriptlang.skript.lang.util.SkriptQueue; import java.lang.reflect.Array; @@ -61,7 +59,7 @@ public class ExprElement extends SimpleExpression { static { //noinspection unchecked - Skript.registerExpression(ExprElement.class, Object.class, ExpressionType.PROPERTY, PATTERNS.getPatterns()); + //Skript.registerExpression(ExprElement.class, Object.class, ExpressionType.PROPERTY, PATTERNS.getPatterns()); } private enum ElementType { diff --git a/src/main/java/org/skriptlang/skript/common/properties/PropertiesModule.java b/src/main/java/org/skriptlang/skript/common/properties/PropertiesModule.java index 81db9eae319..3798e51b72d 100644 --- a/src/main/java/org/skriptlang/skript/common/properties/PropertiesModule.java +++ b/src/main/java/org/skriptlang/skript/common/properties/PropertiesModule.java @@ -1,7 +1,6 @@ package org.skriptlang.skript.common.properties; import ch.njol.skript.Skript; -import ch.njol.skript.SkriptConfig; import org.skriptlang.skript.addon.AddonModule; import org.skriptlang.skript.addon.SkriptAddon; @@ -9,11 +8,6 @@ public class PropertiesModule implements AddonModule { - @Override - public boolean canLoad(SkriptAddon addon) { - return SkriptConfig.useTypeProperties.value(); - } - @Override public void load(SkriptAddon addon) { try { diff --git a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java new file mode 100644 index 00000000000..929318e37d0 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java @@ -0,0 +1,299 @@ +package org.skriptlang.skript.common.properties.expressions; + +import ch.njol.skript.Skript; +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.util.LiteralUtils; +import ch.njol.skript.util.Patterns; +import ch.njol.skript.util.Utils; +import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; +import com.google.common.collect.Iterators; +import org.apache.commons.lang3.ArrayUtils; +import org.bukkit.event.Event; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.PropertyBaseSyntax; +import org.skriptlang.skript.lang.properties.PropertyHandler.ElementHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.ElementsHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.RangedElementsHandler; +import org.skriptlang.skript.lang.properties.PropertyMap; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; + +public class PropExprElements extends SimpleExpression implements PropertyBaseSyntax> { + + private static final Patterns PATTERNS = new Patterns<>(new Object[][]{ + {"[the] first element ([out] of|in) %objects%", ElementsType.FIRST}, + {"[the] last element ([out] of|in) %objects%", ElementsType.LAST}, + {"[the] first %integer% elements ([out] of|in) %objects%", ElementsType.FIRST_X}, + {"[the] last %integer% elements ([out] of|in) %objects%", ElementsType.LAST_X}, + {"[a] random element ([out] of|in) %objects%", ElementsType.RANDOM}, + {"[the] %integer%(st|nd|rd|th) element ([out] of|in) %objects%", ElementsType.ORDINAL}, + {"[the] %integer%(st|nd|rd|th) [to] last element ([out] of|in) %objects%", ElementsType.END_ORDINAL}, + {"[the] elements (from|between) %integer% (to|and) %integer% ([out] of|in) %objects%", ElementsType.RANGE} + }); + + static { + Skript.registerExpression(PropExprElements.class, Object.class, ExpressionType.COMBINED, PATTERNS.getPatterns()); + } + + private enum ElementsType { + FIRST(Property.FIRST_ELEMENT), + LAST(Property.LAST_ELEMENT), + FIRST_X(Property.FIRST_X_ELEMENTS), + LAST_X(Property.LAST_X_ELEMENTS), + RANDOM(Property.RANDOM_ELEMENT), + ORDINAL(Property.ORDINAL_ELEMENT), + END_ORDINAL(Property.END_ORDINAL_ELEMENT), + RANGE(Property.RANGED_ELEMENTS); + + private final Property> property; + + ElementsType(Property> property) { + this.property = property; + } + + } + private ElementsType elementsType; + + private Property> property; + private Expression objects; + private @Nullable Expression startIndex; + private @Nullable Expression endIndex; + private boolean useProperty = false; + + private Class[] returnTypes = new Class[] {Object.class}; + private Class returnType = Object.class; + private PropertyMap> properties; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + elementsType = PATTERNS.getInfo(matchedPattern); + property = elementsType.property; + int objectIndex = 0; + switch (elementsType) { + case FIRST_X, LAST_X, ORDINAL, END_ORDINAL -> { + //noinspection unchecked + startIndex = (Expression) exprs[0]; + objectIndex = 1; + } + case RANGE -> { + //noinspection unchecked + startIndex = (Expression) exprs[0]; + //noinspection unchecked + endIndex = (Expression) exprs[1]; + objectIndex = 2; + } + } + + Expression defendedObjects = LiteralUtils.defendExpression(exprs[objectIndex]); + objects = defendedObjects; + if (!objects.isSingle()) { + return LiteralUtils.canInitSafely(objects); + } + + objects = PropertyBaseSyntax.asProperty(property, defendedObjects); + if (objects == null) { + objects = defendedObjects; + return LiteralUtils.canInitSafely(objects); + } + + //noinspection unchecked + properties = (PropertyMap>) PropertyBaseSyntax.getPossiblePropertyInfos(property, objects); + if (properties.isEmpty()) { + return LiteralUtils.canInitSafely(objects); + } + + returnTypes = getPropertyReturnTypes(properties, ElementHandler::possibleReturnTypes); + if (returnTypes.length == 0) { + returnType = Object.class; + returnTypes = new Class[] {returnType}; + } else { + returnType = Utils.getSuperType(returnTypes); + } + useProperty = true; + + return LiteralUtils.canInitSafely(objects); + } + + protected Class @NotNull [] getPropertyReturnTypes( + @NotNull PropertyMap> properties, + Function, Class[]> getReturnType + ) { + return properties.values().stream() + .flatMap((propertyInfo) -> Arrays.stream(getReturnType.apply(propertyInfo.handler()))) + .filter(type -> type != Object.class) + .toArray(Class[]::new); + } + + @Override + protected Object @Nullable [] get(Event event) { + if (useProperty) { + switch (elementsType) { + case FIRST_X, LAST_X, ORDINAL, END_ORDINAL -> { + assert startIndex != null; + Integer start = startIndex.getSingle(event); + if (start == null) + return null; + return objects.stream(event) + .flatMap(source -> { + ElementsHandler handler = (ElementsHandler) properties.getHandler(source.getClass()); + if (handler == null) + return null; + Object[] elements = getElements(handler, source, start); + if (elements == null) + return null; + return Stream.of(elements); + }) + .filter(Objects::nonNull) + .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); + } + case RANGE -> { + assert startIndex != null; + assert endIndex != null; + Integer start = startIndex.getSingle(event); + if (start == null) + return null; + Integer end = endIndex.getSingle(event); + if (end == null) + return null; + return objects.stream(event) + .flatMap(source -> { + RangedElementsHandler handler = (RangedElementsHandler) properties.getHandler(source.getClass()); + if (handler == null) + return null; + Object[] elements = getElements(handler, source, start, end); + if (elements == null) + return null; + return Stream.of(elements); + }) + .filter(Objects::nonNull) + .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); + } + default -> { + return objects.stream(event) + .flatMap(source -> { + ElementHandler handler = properties.getHandler(source.getClass()); + if (handler == null) + return null; + Object element = getElement(handler, source); + if (element == null) + return null; + return Stream.of(element); + }) + .filter(Objects::nonNull) + .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); + } + } + } + Iterator iterator = objects.iterator(event); + if (iterator == null || !iterator.hasNext()) + return null; + Integer start = 0; + Integer end = 0; + if (startIndex != null) { + start = startIndex.getSingle(event); + if (start == null || (start <= 0 && elementsType != ElementsType.RANGE)) + return null; + } + if (endIndex != null) { + end = endIndex.getSingle(event); + if (end == null) + return null; + } + Object element = null; + Object[] elements; + switch (elementsType) { + case FIRST -> element = iterator.next(); + case LAST -> element = Iterators.getLast(iterator); + case RANDOM -> element = CollectionUtils.getRandom(Iterators.toArray(iterator, Object.class)); + case ORDINAL -> { + Iterators.advance(iterator, start - 1); + if (!iterator.hasNext()) + return null; + element = iterator.next(); + } + case END_ORDINAL -> { + elements = Iterators.toArray(iterator, Object.class); + if (start > elements.length) + return null; + element = elements[elements.length - start]; + } + case FIRST_X -> { + return Iterators.toArray(Iterators.limit(iterator, start), Object.class); + } + case LAST_X -> { + elements = Iterators.toArray(iterator, Object.class); + start = Math.min(start, elements.length); + return CollectionUtils.subarray(elements, elements.length - start, elements.length); + } + case RANGE -> { + elements = Iterators.toArray(iterator, Object.class); + boolean reverse = start > end; + int from = Math.min(start, end) - 1; + int to = Math.max(start, end); + elements = CollectionUtils.subarray(elements, from, to); + if (reverse) + ArrayUtils.reverse(elements); + return elements; + } + } + + elements = new Object[] {element}; + return elements; + } + + @SuppressWarnings("unchecked") + private @Nullable Object getElement(ElementHandler handler, T source) { + return ((ElementHandler) handler).getElement(source); + } + + @SuppressWarnings("unchecked") + private Object @Nullable [] getElements(ElementsHandler handler, T source, int start) { + return ((ElementsHandler) handler).getElements(source, start); + } + + @SuppressWarnings("unchecked") + private Object @Nullable [] getElements(RangedElementsHandler handler, T source, int start, int end) { + return ((RangedElementsHandler) handler).getElements(source, start, end); + } + + @Override + public boolean isSingle() { + return switch (elementsType) { + case FIRST, LAST, ORDINAL, END_ORDINAL, RANDOM -> true; + default -> false; + }; + } + + @Override + public Class getReturnType() { + return returnType; + } + + @Override + public Class[] possibleReturnTypes() { + return returnTypes; + } + + @Override + public @NotNull Property> getProperty() { + //noinspection unchecked + return (Property>) property; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return ""; + } + +} diff --git a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java index cd2d2ccf88b..32c5789b247 100644 --- a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java +++ b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java @@ -7,18 +7,24 @@ import ch.njol.skript.classes.Serializer; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; +import ch.njol.util.coll.CollectionUtils; import ch.njol.yggdrasil.Fields; +import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.properties.PropertyHandler.ConditionPropertyHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.ElementHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.ElementsHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.RangedElementsHandler; import org.skriptlang.skript.lang.util.SkriptQueue; import java.io.StreamCorruptedException; import java.util.Arrays; import java.util.List; +import java.util.concurrent.ThreadLocalRandom; @ApiStatus.Internal public class QueueClassInfo extends ClassInfo { @@ -48,7 +54,39 @@ public QueueClassInfo() { .property(Property.IS_EMPTY, "Whether a queue is empty, i.e. whether there are no elements in the queue.", Skript.instance(), - ConditionPropertyHandler.of(SkriptQueue::isEmpty)); + ConditionPropertyHandler.of(SkriptQueue::isEmpty)) + .property(Property.FIRST_ELEMENT, + "", + Skript.instance(), + new QueueFirstElementHandler()) + .property(Property.LAST_ELEMENT, + "", + Skript.instance(), + new QueueLastElementHandler()) + .property(Property.RANDOM_ELEMENT, + "", + Skript.instance(), + new QueueRandomElementHandler()) + .property(Property.ORDINAL_ELEMENT, + "", + Skript.instance(), + new QueueOrdinalElementHandler()) + .property(Property.END_ORDINAL_ELEMENT, + "", + Skript.instance(), + new QueueEndOrdinalElementHandler()) + .property(Property.FIRST_X_ELEMENTS, + "", + Skript.instance(), + new QueueFirstXElementsHandler()) + .property(Property.LAST_X_ELEMENTS, + "", + Skript.instance(), + new QueueLastXElementsHandler()) + .property(Property.RANGED_ELEMENTS, + "", + Skript.instance(), + new QueueRangedElementsHandler()); } private static class QueueChanger implements Changer { @@ -143,4 +181,120 @@ public Integer convert(SkriptQueue propertyHolder) { // } + private static class QueueFirstElementHandler implements ElementHandler { + // + @Override + public @Nullable Object getElement(SkriptQueue queue) { + return queue.pollFirst(); + } + + @Override + public @NotNull Class returnType() { + return Object.class; + } + // + } + + private static class QueueLastElementHandler implements ElementHandler { + // + @Override + public @Nullable Object getElement(SkriptQueue queue) { + return queue.pollLast(); + } + + @Override + public @NotNull Class returnType() { + return Object.class; + } + // + } + + private static class QueueRandomElementHandler implements ElementHandler { + // + @Override + public @Nullable Object getElement(SkriptQueue queue) { + return queue.removeSafely(ThreadLocalRandom.current().nextInt(0, queue.size())); + } + + @Override + public @NotNull Class returnType() { + return Object.class; + } + // + } + + private static class QueueFirstXElementsHandler implements ElementsHandler { + // + @Override + public Object @Nullable [] getElements(SkriptQueue queue, Integer index) { + return CollectionUtils.array(queue.removeRangeSafely(0, index)); + } + @Override + public @NotNull Class returnType() { + return Object.class; + } + // + } + + private static class QueueLastXElementsHandler implements ElementsHandler { + // + @Override + public Object @Nullable [] getElements(SkriptQueue queue, Integer index) { + return CollectionUtils.array(queue.removeRangeSafely(queue.size() - index, queue.size())); + } + @Override + public @NotNull Class returnType() { + return Object.class; + } + // + } + + private static class QueueOrdinalElementHandler implements ElementsHandler { + // + @Override + public Object @Nullable [] getElements(SkriptQueue queue, Integer index) { + return CollectionUtils.array(queue.removeSafely(index - 1)); + } + + @Override + public @NotNull Class returnType() { + return Object.class; + } + // + } + + private static class QueueEndOrdinalElementHandler implements ElementsHandler { + // + @Override + public Object @Nullable [] getElements(SkriptQueue queue, Integer index) { + return CollectionUtils.array(queue.removeSafely(queue.size() - index)); + } + + @Override + public @NotNull Class returnType() { + return Object.class; + } + // + } + + private static class QueueRangedElementsHandler implements RangedElementsHandler { + // + @Override + public Object @Nullable [] getElements(SkriptQueue queue, Integer start, Integer end) { + boolean reverse = start > end; + int from = Math.min(start, end) - 1; + int to = Math.max(start, end); + Object[] elements = CollectionUtils.array(queue.removeRangeSafely(from , to)); + if (reverse) + ArrayUtils.reverse(elements); + return elements; + } + + @Override + public @NotNull Class returnType() { + return Object.class; + } + // + } + } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java index 01171aafbb8..6620646b669 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -18,7 +18,10 @@ import org.skriptlang.skript.common.types.ScriptClassInfo; import org.skriptlang.skript.lang.properties.PropertyHandler.ConditionPropertyHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.ElementHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.ElementsHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; +import org.skriptlang.skript.lang.properties.PropertyHandler.RangedElementsHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.TypedValuePropertyHandler; import java.util.Locale; @@ -244,6 +247,70 @@ public record PropertyInfo>(Property Skript.instance(), TypedValuePropertyHandler.class); + public static final Property> FIRST_ELEMENT = Property.of( + "first element", + "The first element of something.", + "INSERT VERSION", + Skript.instance(), + ElementHandler.class + ); + + public static final Property> LAST_ELEMENT = Property.of( + "last element", + "The last element of something.", + "INSERT VERSION", + Skript.instance(), + ElementHandler.class + ); + + public static final Property> FIRST_X_ELEMENTS = Property.of( + "first x elements", + "The first x elements of something.", + "INSERT VERSION", + Skript.instance(), + ElementsHandler.class + ); + + public static final Property> LAST_X_ELEMENTS = Property.of( + "last x elements", + "The last x elements of something.", + "INSERT VERSION", + Skript.instance(), + ElementsHandler.class + ); + + public static final Property> RANDOM_ELEMENT = Property.of( + "random element", + "A random element of something.", + "INSERT VERSION", + Skript.instance(), + ElementHandler.class + ); + + public static final Property> ORDINAL_ELEMENT = Property.of( + "ordinal element", + "The element at a index x of something.", + "INSERT VERSION", + Skript.instance(), + ElementsHandler.class + ); + + public static final Property> END_ORDINAL_ELEMENT = Property.of( + "end ordinal element", + "The element at a index x to last of something.", + "INSERT VERSION", + Skript.instance(), + ElementsHandler.class + ); + + public static final Property> RANGED_ELEMENTS = Property.of( + "ranged elements", + "The element between x and y of something.", + "INSERT VERSION", + Skript.instance(), + RangedElementsHandler.class + ); + /** * Register all Skript's default properties. Should be done prior to loading classinfos. */ diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java index 6343cea0ef7..a00fde1cec6 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java @@ -279,4 +279,52 @@ default boolean canContain(Class type) { } } + interface ElementHandler extends PropertyHandler { + + @Nullable ReturnType getElement(Type type); + + /** + * The return type of this property. This is used for type checking and auto-completion. + * If the property can return multiple types, it should return the most general type that encompasses all + * possible return types. + * + * @return The return type of this property. + */ + @NotNull Class returnType(); + + /** + * The possible return types of this property. This is used for type checking and auto-completion. + * The default implementation returns an array containing the type returned by {@link #returnType()}. + * If the property can return multiple types, it should return all possible return types. + * + * @return The possible return types of this property. + */ + default Class @NotNull [] possibleReturnTypes() { + return new Class[]{ returnType() }; + } + + } + + interface ElementsHandler extends ElementHandler { + + ReturnType @Nullable [] getElements(Type type, Integer integer); + + @Override + default @Nullable ReturnType getElement(Type type) { + return null; + } + + } + + interface RangedElementsHandler extends ElementsHandler { + + ReturnType @Nullable [] getElements(Type type, Integer from, Integer to); + + @Override + default ReturnType @Nullable [] getElements(Type type, Integer integer) { + return null; + } + + } + } From 93452e5732571b827f8ddaf977324a1fd7a74faa Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Mon, 27 Oct 2025 22:32:50 -0400 Subject: [PATCH 2/6] Second Commit --- .../njol/skript/expressions/ExprElement.java | 313 ------------------ .../expressions/PropExprElements.java | 186 +++++------ .../skript/common/types/QueueClassInfo.java | 150 +-------- .../skript/lang/properties/Property.java | 68 +--- .../lang/properties/PropertyHandler.java | 30 +- 5 files changed, 116 insertions(+), 631 deletions(-) delete mode 100644 src/main/java/ch/njol/skript/expressions/ExprElement.java diff --git a/src/main/java/ch/njol/skript/expressions/ExprElement.java b/src/main/java/ch/njol/skript/expressions/ExprElement.java deleted file mode 100644 index 5e1d1041ce8..00000000000 --- a/src/main/java/ch/njol/skript/expressions/ExprElement.java +++ /dev/null @@ -1,313 +0,0 @@ -package ch.njol.skript.expressions; - -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.Literal; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.lang.simplification.SimplifiedLiteral; -import ch.njol.skript.lang.util.SimpleExpression; -import ch.njol.skript.registrations.Feature; -import ch.njol.skript.util.LiteralUtils; -import ch.njol.skript.util.Patterns; -import ch.njol.util.Kleenean; -import ch.njol.util.StringUtils; -import ch.njol.util.coll.CollectionUtils; -import com.google.common.collect.Iterators; -import org.apache.commons.lang.ArrayUtils; -import org.bukkit.event.Event; -import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.lang.util.SkriptQueue; - -import java.lang.reflect.Array; -import java.util.Iterator; -import java.util.concurrent.ThreadLocalRandom; - -@Name("Elements") -@Description({ - "The first, last, range or a random element of a set, e.g. a list variable, or a queue.", - "Asking for elements from a queue will also remove them from the queue, see the new queue expression for more information.", - "See also: random expression" -}) -@Examples({ - "broadcast the first 3 elements of {top players::*}", - "set {_last} to last element of {top players::*}", - "set {_random player} to random element out of all players", - "send 2nd last element of {top players::*} to player", - "set {page2::*} to elements from 11 to 20 of {top players::*}", - "broadcast the 1st element in {queue}", - "broadcast the first 3 elements in {queue}" -}) -@Since("2.0, 2.7 (relative to last element), 2.8.0 (range of elements)") -public class ExprElement extends SimpleExpression { - - private static final Patterns PATTERNS = new Patterns<>(new Object[][]{ - {"[the] (first|1:last) element [out] of %objects%", new ElementType[] {ElementType.FIRST_ELEMENT, ElementType.LAST_ELEMENT}}, - {"[the] (first|1:last) %integer% elements [out] of %objects%", new ElementType[] {ElementType.FIRST_X_ELEMENTS, ElementType.LAST_X_ELEMENTS}}, - {"[a] random element [out] of %objects%", new ElementType[] {ElementType.RANDOM}}, - {"[the] %integer%(st|nd|rd|th) [1:[to] last] element [out] of %objects%", new ElementType[] {ElementType.ORDINAL, ElementType.TAIL_END_ORDINAL}}, - {"[the] elements (from|between) %integer% (to|and) %integer% [out] of %objects%", new ElementType[] {ElementType.RANGE}}, - - {"[the] (first|next|1:last) element (of|in) %queue%", new ElementType[] {ElementType.FIRST_ELEMENT, ElementType.LAST_ELEMENT}}, - {"[the] (first|1:last) %integer% elements (of|in) %queue%", new ElementType[] {ElementType.FIRST_X_ELEMENTS, ElementType.LAST_X_ELEMENTS}}, - {"[a] random element (of|in) %queue%", new ElementType[] {ElementType.RANDOM}}, - {"[the] %integer%(st|nd|rd|th) [1:[to] last] element (of|in) %queue%", new ElementType[] {ElementType.ORDINAL, ElementType.TAIL_END_ORDINAL}}, - {"[the] elements (from|between) %integer% (to|and) %integer% (of|in) %queue%", new ElementType[] {ElementType.RANGE}}, - }); - - static { - //noinspection unchecked - //Skript.registerExpression(ExprElement.class, Object.class, ExpressionType.PROPERTY, PATTERNS.getPatterns()); - } - - private enum ElementType { - FIRST_ELEMENT, - LAST_ELEMENT, - FIRST_X_ELEMENTS, - LAST_X_ELEMENTS, - RANDOM, - ORDINAL, - TAIL_END_ORDINAL, - RANGE - } - - private Expression expr; - private @Nullable Expression startIndex, endIndex; - private ElementType type; - private boolean queue; - - @Override - @SuppressWarnings("unchecked") - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - ElementType[] types = PATTERNS.getInfo(matchedPattern); - this.queue = matchedPattern > 4; - if (queue && !this.getParser().hasExperiment(Feature.QUEUES)) - return false; - if (queue) { - this.expr = (Expression) exprs[exprs.length - 1]; - } else { - this.expr = LiteralUtils.defendExpression(exprs[exprs.length - 1]); - } - switch (type = types[parseResult.mark]) { - case RANGE: - endIndex = (Expression) exprs[1]; - case FIRST_X_ELEMENTS, LAST_X_ELEMENTS, ORDINAL, TAIL_END_ORDINAL: - startIndex = (Expression) exprs[0]; - break; - default: - startIndex = null; - break; - } - return queue || LiteralUtils.canInitSafely(expr); - } - - @Override - @SuppressWarnings("unchecked") - protected T @Nullable [] get(Event event) { - if (queue) - return this.getFromQueue(event); - Iterator iterator = expr.iterator(event); - if (iterator == null || !iterator.hasNext()) - return null; - T element = null; - Class returnType = (Class) getReturnType(); - int startIndex = 0, endIndex = 0; - if (this.startIndex != null) { - Integer integer = this.startIndex.getSingle(event); - if (integer == null) - return null; - startIndex = integer; - if (startIndex <= 0 && type != ElementType.RANGE) - return null; - } - if (this.endIndex != null) { - Integer integer = this.endIndex.getSingle(event); - if (integer == null) - return null; - endIndex = integer; - } - T[] elementArray; - switch (type) { - case FIRST_ELEMENT: - element = iterator.next(); - break; - case LAST_ELEMENT: - element = Iterators.getLast(iterator); - break; - case RANDOM: - element = CollectionUtils.getRandom(Iterators.toArray(iterator, returnType)); - break; - case ORDINAL: - Iterators.advance(iterator, startIndex - 1); - if (!iterator.hasNext()) - return null; - element = iterator.next(); - break; - case TAIL_END_ORDINAL: - elementArray = Iterators.toArray(iterator, returnType); - if (startIndex > elementArray.length) - return null; - element = elementArray[elementArray.length - startIndex]; - break; - case FIRST_X_ELEMENTS: - return Iterators.toArray(Iterators.limit(iterator, startIndex), returnType); - case LAST_X_ELEMENTS: - elementArray = Iterators.toArray(iterator, returnType); - startIndex = Math.min(startIndex, elementArray.length); - return CollectionUtils.subarray(elementArray, elementArray.length - startIndex, elementArray.length); - case RANGE: - elementArray = Iterators.toArray(iterator, returnType); - boolean reverse = startIndex > endIndex; - int from = Math.min(startIndex, endIndex) - 1; - int to = Math.max(startIndex, endIndex); - T[] elements = CollectionUtils.subarray(elementArray, from, to); - if (reverse) - ArrayUtils.reverse(elements); - return elements; - } - //noinspection unchecked - elementArray = (T[]) Array.newInstance(getReturnType(), 1); - elementArray[0] = element; - return elementArray; - } - - @SuppressWarnings("unchecked") - private T @Nullable [] getFromQueue(Event event) { - SkriptQueue queue = (SkriptQueue) expr.getSingle(event); - if (queue == null) - return null; - Integer startIndex = 0, endIndex = 0; - if (this.startIndex != null) { - startIndex = this.startIndex.getSingle(event); - if (startIndex == null || startIndex <= 0 && type != ElementType.RANGE) - return null; - } - if (this.endIndex != null) { - endIndex = this.endIndex.getSingle(event); - if (endIndex == null) - return null; - } - return switch (type) { - case FIRST_ELEMENT -> CollectionUtils.array((T) queue.pollFirst()); - case LAST_ELEMENT -> CollectionUtils.array((T) queue.pollLast()); - case RANDOM -> CollectionUtils.array((T) queue.removeSafely(ThreadLocalRandom.current().nextInt(0, queue.size()))); - case ORDINAL -> CollectionUtils.array((T) queue.removeSafely(startIndex - 1)); - case TAIL_END_ORDINAL -> CollectionUtils.array((T) queue.removeSafely(queue.size() - startIndex)); - case FIRST_X_ELEMENTS -> CollectionUtils.array((T[]) queue.removeRangeSafely(0, startIndex)); - case LAST_X_ELEMENTS -> CollectionUtils.array((T[]) queue.removeRangeSafely(queue.size() - startIndex, queue.size())); - case RANGE -> { - boolean reverse = startIndex > endIndex; - T[] elements = CollectionUtils.array((T[]) queue.removeRangeSafely(Math.min(startIndex, endIndex) - 1, Math.max(startIndex, endIndex))); - if (reverse) - ArrayUtils.reverse(elements); - yield elements; - } - }; - } - - @Override - @Nullable - @SuppressWarnings("unchecked") - public Expression getConvertedExpression(Class... to) { - Expression convExpr = expr.getConvertedExpression(to); - if (convExpr == null) - return null; - - ExprElement exprElement = new ExprElement<>(); - exprElement.expr = convExpr; - exprElement.startIndex = startIndex; - exprElement.endIndex = endIndex; - exprElement.type = type; - exprElement.queue = queue; - return exprElement; - } - - @Override - public boolean isSingle() { - return type != ElementType.FIRST_X_ELEMENTS && type != ElementType.LAST_X_ELEMENTS && type != ElementType.RANGE; - } - - @Override - public Class getReturnType() { - if (queue) - return (Class) Object.class; - return expr.getReturnType(); - } - - @Override - public Class[] possibleReturnTypes() { - if (!queue) { - return expr.possibleReturnTypes(); - } - return super.possibleReturnTypes(); - } - - @Override - public boolean canReturn(Class returnType) { - if (!queue) { - return expr.canReturn(returnType); - } - return super.canReturn(returnType); - } - - @Override - public Expression simplify() { - if (!queue && expr instanceof Literal - && type != ElementType.RANDOM - && (startIndex == null || startIndex instanceof Literal) - && (endIndex == null || endIndex instanceof Literal)) { - return SimplifiedLiteral.fromExpression(this); - } - return this; - } - - @Override - public String toString(@Nullable Event event, boolean debug) { - String prefix; - switch (type) { - case FIRST_ELEMENT: - prefix = "the first"; - break; - case LAST_ELEMENT: - prefix = "the last"; - break; - case FIRST_X_ELEMENTS: - assert startIndex != null; - prefix = "the first " + startIndex.toString(event, debug); - break; - case LAST_X_ELEMENTS: - assert startIndex != null; - prefix = "the last " + startIndex.toString(event, debug); - break; - case RANDOM: - prefix = "a random"; - break; - case ORDINAL: - case TAIL_END_ORDINAL: - assert startIndex != null; - prefix = "the "; - // Proper ordinal number - if (startIndex instanceof Literal) { - Integer integer = ((Literal) startIndex).getSingle(); - if (integer == null) - prefix += startIndex.toString(event, debug) + "th"; - else - prefix += StringUtils.fancyOrderNumber(integer); - } else { - prefix += startIndex.toString(event, debug) + "th"; - } - if (type == ElementType.TAIL_END_ORDINAL) - prefix += " last"; - break; - case RANGE: - assert startIndex != null && endIndex != null; - return "the elements from " + startIndex.toString(event, debug) + " to " + endIndex.toString(event, debug) + " of " + expr.toString(event, debug); - default: - throw new IllegalStateException(); - } - return prefix + (isSingle() ? " element" : " elements") + " of " + expr.toString(event, debug); - } - -} diff --git a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java index 929318e37d0..fd739144b4d 100644 --- a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java +++ b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java @@ -1,6 +1,10 @@ package org.skriptlang.skript.common.properties.expressions; import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +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; @@ -18,17 +22,31 @@ import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.properties.PropertyBaseSyntax; import org.skriptlang.skript.lang.properties.PropertyHandler.ElementHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.ElementsHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.RangedElementsHandler; import org.skriptlang.skript.lang.properties.PropertyMap; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Iterator; import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Stream; +@Name("Elements") +@Description(""" + The first, last, random, or ranged elements of a list or container object such as a queue. + Asking for elements from a queue will also remove them from the queue, see the new queue expression for more information. + See also: random expression + """) +@Example("broadcast the first 3 elements of {top players::*}") +@Example("set {_last} to last element of {top players::*}") +@Example("set {_random player} to random element out of all players") +@Example("send 2nd last element of {top players::*} to player") +@Example("set {page2::*} to elements from 11 to 20 of {top players::*}") +@Example("broadcast the 1st element in {queue}") +@Example("broadcast the first 3 elements in {queue}") +@Since("2.0, 2.7 (relative to last element), 2.8.0 (range of elements)") public class PropExprElements extends SimpleExpression implements PropertyBaseSyntax> { private static final Patterns PATTERNS = new Patterns<>(new Object[][]{ @@ -47,25 +65,18 @@ public class PropExprElements extends SimpleExpression implements Proper } private enum ElementsType { - FIRST(Property.FIRST_ELEMENT), - LAST(Property.LAST_ELEMENT), - FIRST_X(Property.FIRST_X_ELEMENTS), - LAST_X(Property.LAST_X_ELEMENTS), - RANDOM(Property.RANDOM_ELEMENT), - ORDINAL(Property.ORDINAL_ELEMENT), - END_ORDINAL(Property.END_ORDINAL_ELEMENT), - RANGE(Property.RANGED_ELEMENTS); - - private final Property> property; - - ElementsType(Property> property) { - this.property = property; - } - + FIRST, + LAST, + FIRST_X, + LAST_X, + RANDOM, + ORDINAL, + END_ORDINAL, + RANGE; } + private ElementsType elementsType; - private Property> property; private Expression objects; private @Nullable Expression startIndex; private @Nullable Expression endIndex; @@ -78,7 +89,6 @@ private enum ElementsType { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { elementsType = PATTERNS.getInfo(matchedPattern); - property = elementsType.property; int objectIndex = 0; switch (elementsType) { case FIRST_X, LAST_X, ORDINAL, END_ORDINAL -> { @@ -101,14 +111,13 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return LiteralUtils.canInitSafely(objects); } - objects = PropertyBaseSyntax.asProperty(property, defendedObjects); + objects = PropertyBaseSyntax.asProperty(Property.ELEMENT, objects); if (objects == null) { objects = defendedObjects; return LiteralUtils.canInitSafely(objects); } - //noinspection unchecked - properties = (PropertyMap>) PropertyBaseSyntax.getPossiblePropertyInfos(property, objects); + properties = PropertyBaseSyntax.getPossiblePropertyInfos(Property.ELEMENT, objects); if (properties.isEmpty()) { return LiteralUtils.canInitSafely(objects); } @@ -137,67 +146,6 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override protected Object @Nullable [] get(Event event) { - if (useProperty) { - switch (elementsType) { - case FIRST_X, LAST_X, ORDINAL, END_ORDINAL -> { - assert startIndex != null; - Integer start = startIndex.getSingle(event); - if (start == null) - return null; - return objects.stream(event) - .flatMap(source -> { - ElementsHandler handler = (ElementsHandler) properties.getHandler(source.getClass()); - if (handler == null) - return null; - Object[] elements = getElements(handler, source, start); - if (elements == null) - return null; - return Stream.of(elements); - }) - .filter(Objects::nonNull) - .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); - } - case RANGE -> { - assert startIndex != null; - assert endIndex != null; - Integer start = startIndex.getSingle(event); - if (start == null) - return null; - Integer end = endIndex.getSingle(event); - if (end == null) - return null; - return objects.stream(event) - .flatMap(source -> { - RangedElementsHandler handler = (RangedElementsHandler) properties.getHandler(source.getClass()); - if (handler == null) - return null; - Object[] elements = getElements(handler, source, start, end); - if (elements == null) - return null; - return Stream.of(elements); - }) - .filter(Objects::nonNull) - .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); - } - default -> { - return objects.stream(event) - .flatMap(source -> { - ElementHandler handler = properties.getHandler(source.getClass()); - if (handler == null) - return null; - Object element = getElement(handler, source); - if (element == null) - return null; - return Stream.of(element); - }) - .filter(Objects::nonNull) - .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); - } - } - } - Iterator iterator = objects.iterator(event); - if (iterator == null || !iterator.hasNext()) - return null; Integer start = 0; Integer end = 0; if (startIndex != null) { @@ -210,6 +158,14 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye if (end == null) return null; } + + if (useProperty) { + return getFromProperty(event, start, end); + } + + Iterator iterator = objects.iterator(event); + if (iterator == null || !iterator.hasNext()) + return null; Object element = null; Object[] elements; switch (elementsType) { @@ -252,19 +208,58 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return elements; } - @SuppressWarnings("unchecked") - private @Nullable Object getElement(ElementHandler handler, T source) { - return ((ElementHandler) handler).getElement(source); - } - - @SuppressWarnings("unchecked") - private Object @Nullable [] getElements(ElementsHandler handler, T source, int start) { - return ((ElementsHandler) handler).getElements(source, start); + private Object @Nullable [] getFromProperty(Event event, int start, int end) { + BiFunction, Type, Object[]> function = getHandlerFunction(start, end); + return objects.stream(event) + .flatMap(source -> { + ElementHandler handler = properties.getHandler(source.getClass()); + if (handler == null) + return null; + //noinspection unchecked + Object[] elements = function.apply((ElementHandler) handler, (Type) source); + if (elements == null || elements.length == 0) + return null; + return Stream.of(elements); + }) + .filter(Objects::nonNull) + .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); } - @SuppressWarnings("unchecked") - private Object @Nullable [] getElements(RangedElementsHandler handler, T source, int start, int end) { - return ((RangedElementsHandler) handler).getElements(source, start, end); + private BiFunction, Type, Object[]> getHandlerFunction(Integer start, Integer end) { + return switch (elementsType) { + case FIRST -> ( handler, type) -> + CollectionUtils.array(handler.get(type, 0)); + case LAST -> (handler, type) -> + CollectionUtils.array(handler.get(type, handler.size(type) - 1)); + case ORDINAL -> (handler, type) -> + CollectionUtils.array(handler.get(type, start - 1)); + case END_ORDINAL -> (handler, type) -> + CollectionUtils.array(handler.get(type, handler.size(type) - start)); + case RANDOM -> (handler, type) -> + CollectionUtils.array(handler.get(type, ThreadLocalRandom.current().nextInt(0, handler.size(type)))); + case FIRST_X -> (handler, type) -> + handler.get(type, 0, Math.min(handler.size(type), start)); + case LAST_X -> (handler, type) -> { + int size = handler.size(type); + int lastStart = Math.min(start, size); + return handler.get(type, size - lastStart, size); + }; + case RANGE -> { + boolean reverse = start > end; + int from = Math.min(start, end) - 1; + int to = (Math.max(start, end)); + yield (handler, type) -> { + int size = handler.size(type); + if (from > size) + return null; + int stop = Math.min(to, size); + Object[] objects = handler.get(type, from, stop); + if (reverse) + ArrayUtils.reverse(objects); + return objects; + }; + } + }; } @Override @@ -287,8 +282,7 @@ public Class[] possibleReturnTypes() { @Override public @NotNull Property> getProperty() { - //noinspection unchecked - return (Property>) property; + return Property.ELEMENT; } @Override diff --git a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java index 32c5789b247..e1d340765e1 100644 --- a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java +++ b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java @@ -7,24 +7,19 @@ import ch.njol.skript.classes.Serializer; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; -import ch.njol.util.coll.CollectionUtils; import ch.njol.yggdrasil.Fields; -import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.properties.PropertyHandler.ConditionPropertyHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.ElementHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.ElementsHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.RangedElementsHandler; import org.skriptlang.skript.lang.util.SkriptQueue; import java.io.StreamCorruptedException; import java.util.Arrays; import java.util.List; -import java.util.concurrent.ThreadLocalRandom; @ApiStatus.Internal public class QueueClassInfo extends ClassInfo { @@ -55,38 +50,13 @@ public QueueClassInfo() { "Whether a queue is empty, i.e. whether there are no elements in the queue.", Skript.instance(), ConditionPropertyHandler.of(SkriptQueue::isEmpty)) - .property(Property.FIRST_ELEMENT, - "", + .property(Property.ELEMENT, + """ + Elements of a queue. + Asking for elements from a queue will also remove them from the queue, see the new queue expression for more information. + """, Skript.instance(), - new QueueFirstElementHandler()) - .property(Property.LAST_ELEMENT, - "", - Skript.instance(), - new QueueLastElementHandler()) - .property(Property.RANDOM_ELEMENT, - "", - Skript.instance(), - new QueueRandomElementHandler()) - .property(Property.ORDINAL_ELEMENT, - "", - Skript.instance(), - new QueueOrdinalElementHandler()) - .property(Property.END_ORDINAL_ELEMENT, - "", - Skript.instance(), - new QueueEndOrdinalElementHandler()) - .property(Property.FIRST_X_ELEMENTS, - "", - Skript.instance(), - new QueueFirstXElementsHandler()) - .property(Property.LAST_X_ELEMENTS, - "", - Skript.instance(), - new QueueLastXElementsHandler()) - .property(Property.RANGED_ELEMENTS, - "", - Skript.instance(), - new QueueRangedElementsHandler()); + new QueueElementHandler()); } private static class QueueChanger implements Changer { @@ -181,120 +151,26 @@ public Integer convert(SkriptQueue propertyHolder) { // } - private static class QueueFirstElementHandler implements ElementHandler { - // - @Override - public @Nullable Object getElement(SkriptQueue queue) { - return queue.pollFirst(); - } - - @Override - public @NotNull Class returnType() { - return Object.class; - } - // - } - - private static class QueueLastElementHandler implements ElementHandler { - // - @Override - public @Nullable Object getElement(SkriptQueue queue) { - return queue.pollLast(); - } - - @Override - public @NotNull Class returnType() { - return Object.class; - } - // - } - - private static class QueueRandomElementHandler implements ElementHandler { - // - @Override - public @Nullable Object getElement(SkriptQueue queue) { - return queue.removeSafely(ThreadLocalRandom.current().nextInt(0, queue.size())); - } - - @Override - public @NotNull Class returnType() { - return Object.class; - } - // - } - - private static class QueueFirstXElementsHandler implements ElementsHandler { - // + private static class QueueElementHandler implements ElementHandler { @Override - public Object @Nullable [] getElements(SkriptQueue queue, Integer index) { - return CollectionUtils.array(queue.removeRangeSafely(0, index)); + public @Nullable Object get(SkriptQueue queue, Integer index) { + return queue.removeSafely(index); } - @Override - public @NotNull Class returnType() { - return Object.class; - } - // - } - private static class QueueLastXElementsHandler implements ElementsHandler { - // - @Override - public Object @Nullable [] getElements(SkriptQueue queue, Integer index) { - return CollectionUtils.array(queue.removeRangeSafely(queue.size() - index, queue.size())); - } @Override - public @NotNull Class returnType() { - return Object.class; + public Object @Nullable [] get(SkriptQueue queue, Integer start, Integer end) { + return queue.removeRangeSafely(start, end); } - // - } - private static class QueueOrdinalElementHandler implements ElementsHandler { - // @Override - public Object @Nullable [] getElements(SkriptQueue queue, Integer index) { - return CollectionUtils.array(queue.removeSafely(index - 1)); + public int size(SkriptQueue queue) { + return queue.size(); } @Override public @NotNull Class returnType() { return Object.class; } - // - } - - private static class QueueEndOrdinalElementHandler implements ElementsHandler { - // - @Override - public Object @Nullable [] getElements(SkriptQueue queue, Integer index) { - return CollectionUtils.array(queue.removeSafely(queue.size() - index)); - } - - @Override - public @NotNull Class returnType() { - return Object.class; - } - // - } - - private static class QueueRangedElementsHandler implements RangedElementsHandler { - // - @Override - public Object @Nullable [] getElements(SkriptQueue queue, Integer start, Integer end) { - boolean reverse = start > end; - int from = Math.min(start, end) - 1; - int to = Math.max(start, end); - Object[] elements = CollectionUtils.array(queue.removeRangeSafely(from , to)); - if (reverse) - ArrayUtils.reverse(elements); - return elements; - } - - @Override - public @NotNull Class returnType() { - return Object.class; - } - // } } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java index 6620646b669..0eccbe7c289 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -19,9 +19,7 @@ import org.skriptlang.skript.lang.properties.PropertyHandler.ConditionPropertyHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.ContainsHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.ElementHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.ElementsHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.RangedElementsHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.TypedValuePropertyHandler; import java.util.Locale; @@ -247,70 +245,17 @@ public record PropertyInfo>(Property Skript.instance(), TypedValuePropertyHandler.class); - public static final Property> FIRST_ELEMENT = Property.of( - "first element", - "The first element of something.", - "INSERT VERSION", - Skript.instance(), - ElementHandler.class - ); - - public static final Property> LAST_ELEMENT = Property.of( - "last element", - "The last element of something.", - "INSERT VERSION", - Skript.instance(), - ElementHandler.class - ); - - public static final Property> FIRST_X_ELEMENTS = Property.of( - "first x elements", - "The first x elements of something.", - "INSERT VERSION", - Skript.instance(), - ElementsHandler.class - ); - - public static final Property> LAST_X_ELEMENTS = Property.of( - "last x elements", - "The last x elements of something.", - "INSERT VERSION", - Skript.instance(), - ElementsHandler.class - ); - - public static final Property> RANDOM_ELEMENT = Property.of( - "random element", - "A random element of something.", + /** + * A property for something that can contain things. + */ + public static final Property> ELEMENT = Property.of( + "element", + "something that contains elements.", "INSERT VERSION", Skript.instance(), ElementHandler.class ); - public static final Property> ORDINAL_ELEMENT = Property.of( - "ordinal element", - "The element at a index x of something.", - "INSERT VERSION", - Skript.instance(), - ElementsHandler.class - ); - - public static final Property> END_ORDINAL_ELEMENT = Property.of( - "end ordinal element", - "The element at a index x to last of something.", - "INSERT VERSION", - Skript.instance(), - ElementsHandler.class - ); - - public static final Property> RANGED_ELEMENTS = Property.of( - "ranged elements", - "The element between x and y of something.", - "INSERT VERSION", - Skript.instance(), - RangedElementsHandler.class - ); - /** * Register all Skript's default properties. Should be done prior to loading classinfos. */ @@ -323,6 +268,7 @@ public static void registerDefaultProperties() { NUMBER.register(); IS_EMPTY.register(); TYPED_VALUE.register(); + ELEMENT.register(); } } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java index a00fde1cec6..bddfdeea12f 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java @@ -280,8 +280,12 @@ default boolean canContain(Class type) { } interface ElementHandler extends PropertyHandler { + // + @Nullable ReturnType get(Type type, Integer index); - @Nullable ReturnType getElement(Type type); + ReturnType @Nullable [] get(Type type, Integer start, Integer end); + + int size(Type type); /** * The return type of this property. This is used for type checking and auto-completion. @@ -302,29 +306,7 @@ interface ElementHandler extends PropertyHandler { default Class @NotNull [] possibleReturnTypes() { return new Class[]{ returnType() }; } - - } - - interface ElementsHandler extends ElementHandler { - - ReturnType @Nullable [] getElements(Type type, Integer integer); - - @Override - default @Nullable ReturnType getElement(Type type) { - return null; - } - - } - - interface RangedElementsHandler extends ElementsHandler { - - ReturnType @Nullable [] getElements(Type type, Integer from, Integer to); - - @Override - default ReturnType @Nullable [] getElements(Type type, Integer integer) { - return null; - } - + // } } From 53feb0a1a814e5fff4b349c4a7c23a2fc7cc684e Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Tue, 28 Oct 2025 08:24:27 -0400 Subject: [PATCH 3/6] Third Commit --- .../expressions/PropExprElements.java | 35 ++++++- .../skript/common/types/QueueClassInfo.java | 6 ++ .../skript/lang/properties/Property.java | 2 +- .../lang/properties/PropertyHandler.java | 97 +++++++++++-------- 4 files changed, 96 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java index fd739144b4d..0ea09efd5a7 100644 --- a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java +++ b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java @@ -8,6 +8,7 @@ import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.util.LiteralUtils; import ch.njol.skript.util.Patterns; @@ -208,6 +209,14 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return elements; } + /** + * Helper method for getting values from properties. + * @param event The current {@link Event}. + * @param start The start index. Used for FIRST_X, LAST_X, ORDINAL, END_ORDINAL and RANGE. + * @param end The end index. Used for RANGE + * @return The returned objects from the property handlers. + * @param The type of handler and object. + */ private Object @Nullable [] getFromProperty(Event event, int start, int end) { BiFunction, Type, Object[]> function = getHandlerFunction(start, end); return objects.stream(event) @@ -225,9 +234,16 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye .toArray(size -> (Object[]) Array.newInstance(getReturnType(), size)); } + /** + * Helper method for grabbing the function that handles how the property handler should be called based on the {@link ElementsType}. + * @param start The start index. Used for FIRST_X, LAST_X, ORDINAL, END_ORDINAL and RANGE. + * @param end The end index. Used for RANGE + * @return The function used for handlers. + * @param The type of handler and object. + */ private BiFunction, Type, Object[]> getHandlerFunction(Integer start, Integer end) { return switch (elementsType) { - case FIRST -> ( handler, type) -> + case FIRST -> (handler, type) -> CollectionUtils.array(handler.get(type, 0)); case LAST -> (handler, type) -> CollectionUtils.array(handler.get(type, handler.size(type) - 1)); @@ -247,7 +263,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye case RANGE -> { boolean reverse = start > end; int from = Math.min(start, end) - 1; - int to = (Math.max(start, end)); + int to = Math.max(start, end); yield (handler, type) -> { int size = handler.size(type); if (from > size) @@ -287,7 +303,20 @@ public Class[] possibleReturnTypes() { @Override public String toString(@Nullable Event event, boolean debug) { - return ""; + SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug); + builder.append("the"); + switch (elementsType) { + case FIRST -> builder.append("first element"); + case LAST -> builder.append("last element"); + case RANDOM -> builder.append("random element"); + case ORDINAL -> builder.append(startIndex, "element"); + case END_ORDINAL -> builder.append(startIndex, "last element"); + case FIRST_X -> builder.append("first", startIndex, "elements"); + case LAST_X -> builder.append("last", startIndex, "elements"); + case RANGE -> builder.append("elements between", startIndex, "and", endIndex); + } + builder.append("out of", objects); + return builder.toString(); } } diff --git a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java index e1d340765e1..14a2a03105c 100644 --- a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java +++ b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java @@ -152,8 +152,13 @@ public Integer convert(SkriptQueue propertyHolder) { } private static class QueueElementHandler implements ElementHandler { + // @Override public @Nullable Object get(SkriptQueue queue, Integer index) { + if (index == 0) + return queue.pollFirst(); + if (index == queue.size() - 1) + return queue.pollLast(); return queue.removeSafely(index); } @@ -171,6 +176,7 @@ public int size(SkriptQueue queue) { public @NotNull Class returnType() { return Object.class; } + // } } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java index 0eccbe7c289..c44f59a2af4 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -250,7 +250,7 @@ public record PropertyInfo>(Property */ public static final Property> ELEMENT = Property.of( "element", - "something that contains elements.", + "Something that contains elements.", "INSERT VERSION", Skript.instance(), ElementHandler.class diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java index bddfdeea12f..3cf22acce9a 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java @@ -58,6 +58,36 @@ default boolean init(Expression parentExpression, ParserInstance parser) { return true; } + /** + * A handler to specify that it can return something and what can be returned. + * + * @param The type of object this property can be applied to. + * @param The type of object that is returned by this property. + */ + interface ReturnablePropertyHandler extends PropertyHandler { + + /** + * The return type of this property. This is used for type checking and auto-completion. + * If the property can return multiple types, it should return the most general type that encompasses all + * possible return types. + * + * @return The return type of this property. + */ + @NotNull Class returnType(); + + /** + * The possible return types of this property. This is used for type checking and auto-completion. + * The default implementation returns an array containing the type returned by {@link #returnType()}. + * If the property can return multiple types, it should return all possible return types. + * + * @return The possible return types of this property. + */ + default Class @NotNull [] possibleReturnTypes() { + return new Class[]{ returnType() }; + } + + } + /** * A handler that can get and optionally change a property value. This interface is suitable for properties that act * like expressions, such as "name", "display name", etc. Properties that use this interface should also use @@ -68,7 +98,7 @@ default boolean init(Expression parentExpression, ParserInstance parser) { * @see PropertyBaseExpression */ @ApiStatus.Experimental - interface ExpressionPropertyHandler extends PropertyHandler { + interface ExpressionPropertyHandler extends ReturnablePropertyHandler { /** * Converts the given object to the property value. This method may return arrays if the property is multi-valued. @@ -117,26 +147,6 @@ default boolean requiresSourceExprChange() { return false; } - /** - * The return type of this property. This is used for type checking and auto-completion. - * If the property can return multiple types, it should return the most general type that encompasses all - * possible return types. - * - * @return The return type of this property. - */ - @NotNull Class returnType(); - - /** - * The possible return types of this property. This is used for type checking and auto-completion. - * The default implementation returns an array containing the type returned by {@link #returnType()}. - * If the property can return multiple types, it should return all possible return types. - * - * @return The possible return types of this property. - */ - default Class @NotNull [] possibleReturnTypes() { - return new Class[]{ returnType() }; - } - /** * Creates a simple property handler from the given converter function and return type. * This is a convenience method for creating property handlers that only need to convert @@ -279,34 +289,41 @@ default boolean canContain(Class type) { } } - interface ElementHandler extends PropertyHandler { - // - @Nullable ReturnType get(Type type, Integer index); - - ReturnType @Nullable [] get(Type type, Integer start, Integer end); + /** + * A handler for getting elements/objects contains within another object. + * + * @param The type of object this property can be applied to. + * @param The type of object that is returned by this property. + */ + interface ElementHandler extends ReturnablePropertyHandler { - int size(Type type); + /** + * Retrieve an element at the specified {@code index}. + * + * @param type The object that contains elements. + * @param index The index to retrieve an element from. + * @return The retrieved element. + */ + @Nullable ReturnType get(Type type, Integer index); /** - * The return type of this property. This is used for type checking and auto-completion. - * If the property can return multiple types, it should return the most general type that encompasses all - * possible return types. + * Retrieve elements from {@code start} to {@code end}. * - * @return The return type of this property. + * @param type The object that contains elements. + * @param start The starting index. + * @param end The end index. + * @return The retrieved elements. */ - @NotNull Class returnType(); + ReturnType @Nullable [] get(Type type, Integer start, Integer end); /** - * The possible return types of this property. This is used for type checking and auto-completion. - * The default implementation returns an array containing the type returned by {@link #returnType()}. - * If the property can return multiple types, it should return all possible return types. + * Retrieve the number of elements. * - * @return The possible return types of this property. + * @param type The object that contains elements. + * @return The number of elements contained. */ - default Class @NotNull [] possibleReturnTypes() { - return new Class[]{ returnType() }; - } - // + int size(Type type); + } } From f6598985dc8655b1a9d3c94554e77caa62319edc Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Tue, 28 Oct 2025 08:46:32 -0400 Subject: [PATCH 4/6] Fourth Commit --- .../expressions/PropExprElements.java | 14 +------- .../expressions/PropExprValueOf.java | 2 +- .../properties/PropertyBaseExpression.java | 17 +++++---- .../lang/properties/PropertyBaseSyntax.java | 36 +++++++++++++++++++ 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java index 0ea09efd5a7..935cd77950a 100644 --- a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java +++ b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java @@ -26,12 +26,10 @@ import org.skriptlang.skript.lang.properties.PropertyMap; import java.lang.reflect.Array; -import java.util.Arrays; import java.util.Iterator; import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; import java.util.function.BiFunction; -import java.util.function.Function; import java.util.stream.Stream; @Name("Elements") @@ -123,7 +121,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return LiteralUtils.canInitSafely(objects); } - returnTypes = getPropertyReturnTypes(properties, ElementHandler::possibleReturnTypes); + returnTypes = PropertyBaseSyntax.getPropertyReturnTypes(properties); if (returnTypes.length == 0) { returnType = Object.class; returnTypes = new Class[] {returnType}; @@ -135,16 +133,6 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return LiteralUtils.canInitSafely(objects); } - protected Class @NotNull [] getPropertyReturnTypes( - @NotNull PropertyMap> properties, - Function, Class[]> getReturnType - ) { - return properties.values().stream() - .flatMap((propertyInfo) -> Arrays.stream(getReturnType.apply(propertyInfo.handler()))) - .filter(type -> type != Object.class) - .toArray(Class[]::new); - } - @Override protected Object @Nullable [] get(Event event) { Integer start = 0; diff --git a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprValueOf.java b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprValueOf.java index 98845974103..8272ca73a9e 100644 --- a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprValueOf.java +++ b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprValueOf.java @@ -74,7 +74,7 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is // determine possible return types if (type == null) { - returnTypes = getPropertyReturnTypes(properties, TypedValuePropertyHandler::possibleReturnTypes); + returnTypes = getPropertyReturnTypes(properties); returnType = Utils.getSuperType(returnTypes); } else { returnTypes = new Class[]{ type.getC() }; diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java index 5fa75b89542..7591c9d6dab 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseExpression.java @@ -20,7 +20,13 @@ import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; import java.lang.reflect.Array; -import java.util.*; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -80,16 +86,13 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is } // determine possible return types - returnTypes = getPropertyReturnTypes(properties, Handler::possibleReturnTypes); + returnTypes = getPropertyReturnTypes(properties); returnType = Utils.getSuperType(returnTypes); return LiteralUtils.canInitSafely(expr); } - protected Class @NotNull [] getPropertyReturnTypes(@NotNull PropertyMap properties, Function[]> getReturnType) { - return properties.values().stream() - .flatMap((propertyInfo) -> Arrays.stream(getReturnType.apply(propertyInfo.handler()))) - .filter(type -> type != Object.class) - .toArray(Class[]::new); + protected Class @NotNull [] getPropertyReturnTypes(PropertyMap properties) { + return PropertyBaseSyntax.getPropertyReturnTypes(properties); } @Override diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseSyntax.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseSyntax.java index c972dd8b3b6..2d990534cb9 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseSyntax.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseSyntax.java @@ -7,10 +7,13 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.PropertyHandler.ReturnablePropertyHandler; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.function.Function; /** * A base interface for syntaxes dealing with properties to extend and use for common utilities. @@ -141,4 +144,37 @@ static > PropertyMap getPossibleProp return propertyInfos; } + + /** + * Get all possible return types from the properties contained in {@code properties}. + * Uses {@link ReturnablePropertyHandler#possibleReturnTypes()} + * + * @param properties A {@link PropertyMap} containing the possible properties. + * @return All possible return types. + * @param the type of returnable property handler. + */ + static > Class @NotNull [] getPropertyReturnTypes( + @NotNull PropertyMap properties + ) { + return getPropertyReturnTypes(properties, ReturnablePropertyHandler::possibleReturnTypes); + } + + /** + * Get all possible return types from the properties contained in {@code properties}. + * + * @param properties A {@link PropertyMap} containing the possible properties. + * @param getReturnType The function used to get the return types from {@link Handler}. + * @return All possible return types. + * @param the type of property handler. + */ + static > Class @NotNull [] getPropertyReturnTypes( + @NotNull PropertyMap properties, + Function[]> getReturnType + ) { + return properties.values().stream() + .flatMap((propertyInfo) -> Arrays.stream(getReturnType.apply(propertyInfo.handler()))) + .filter(type -> type != Object.class) + .toArray(Class[]::new); + } + } From cf2e69ba17cf5c1d322e2b81055bfae084214851 Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Thu, 1 Jan 2026 16:09:06 -0500 Subject: [PATCH 5/6] Update --- .../expressions/PropExprElements.java | 8 +- .../skript/common/types/QueueClassInfo.java | 6 +- .../skript/lang/properties/Property.java | 5 +- .../lang/properties/PropertyBaseSyntax.java | 2 +- .../lang/properties/PropertyHandler.java | 329 ------------------ .../properties/handlers/ElementHandler.java | 43 +++ .../base/ExpressionPropertyHandler.java | 22 +- .../base/ReturnablePropertyHandler.java | 35 ++ 8 files changed, 89 insertions(+), 361 deletions(-) delete mode 100644 src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/handlers/ElementHandler.java create mode 100644 src/main/java/org/skriptlang/skript/lang/properties/handlers/base/ReturnablePropertyHandler.java diff --git a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java index 935cd77950a..c83c7f80c2f 100644 --- a/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java +++ b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java @@ -22,8 +22,8 @@ import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.properties.Property; import org.skriptlang.skript.lang.properties.PropertyBaseSyntax; -import org.skriptlang.skript.lang.properties.PropertyHandler.ElementHandler; import org.skriptlang.skript.lang.properties.PropertyMap; +import org.skriptlang.skript.lang.properties.handlers.ElementHandler; import java.lang.reflect.Array; import java.util.Iterator; @@ -110,13 +110,13 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return LiteralUtils.canInitSafely(objects); } - objects = PropertyBaseSyntax.asProperty(Property.ELEMENT, objects); + objects = PropertyBaseSyntax.asProperty(Property.ORDERED_ELEMENTS, objects); if (objects == null) { objects = defendedObjects; return LiteralUtils.canInitSafely(objects); } - properties = PropertyBaseSyntax.getPossiblePropertyInfos(Property.ELEMENT, objects); + properties = PropertyBaseSyntax.getPossiblePropertyInfos(Property.ORDERED_ELEMENTS, objects); if (properties.isEmpty()) { return LiteralUtils.canInitSafely(objects); } @@ -286,7 +286,7 @@ public Class[] possibleReturnTypes() { @Override public @NotNull Property> getProperty() { - return Property.ELEMENT; + return Property.ORDERED_ELEMENTS; } @Override diff --git a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java index 313a35d16f8..4dcd028356d 100644 --- a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java +++ b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java @@ -12,9 +12,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.properties.Property; -import org.skriptlang.skript.lang.properties.PropertyHandler.ConditionPropertyHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.ElementHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.ExpressionPropertyHandler; +import org.skriptlang.skript.lang.properties.handlers.ElementHandler; import org.skriptlang.skript.lang.properties.handlers.base.ConditionPropertyHandler; import org.skriptlang.skript.lang.properties.handlers.base.ExpressionPropertyHandler; import org.skriptlang.skript.lang.util.SkriptQueue; @@ -52,7 +50,7 @@ public QueueClassInfo() { "Whether a queue is empty, i.e. whether there are no elements in the queue.", Skript.instance(), ConditionPropertyHandler.of(SkriptQueue::isEmpty)) - .property(Property.ELEMENT, + .property(Property.ORDERED_ELEMENTS, """ Elements of a queue. Asking for elements from a queue will also remove them from the queue, see the new queue expression for more information. diff --git a/src/main/java/org/skriptlang/skript/lang/properties/Property.java b/src/main/java/org/skriptlang/skript/lang/properties/Property.java index 53bb49772ac..981618e45bb 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -17,6 +17,7 @@ import org.skriptlang.skript.common.types.QueueClassInfo; import org.skriptlang.skript.common.types.ScriptClassInfo; import org.skriptlang.skript.lang.properties.handlers.ContainsHandler; +import org.skriptlang.skript.lang.properties.handlers.ElementHandler; import org.skriptlang.skript.lang.properties.handlers.TypedValueHandler; import org.skriptlang.skript.lang.properties.handlers.WXYZHandler; import org.skriptlang.skript.lang.properties.handlers.base.ConditionPropertyHandler; @@ -279,7 +280,7 @@ public record PropertyInfo>(Property /** * A property for something that can contain things. */ - public static final Property> ELEMENT = Property.of( + public static final Property> ORDERED_ELEMENTS = Property.of( "element", "Something that contains elements.", "INSERT VERSION", @@ -301,7 +302,7 @@ public static void registerDefaultProperties() { TYPED_VALUE.register(); SCALE.register(); SPEED.register(); - ELEMENT.register(); + ORDERED_ELEMENTS.register(); } } diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseSyntax.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseSyntax.java index abde276b9a0..fcaad887f53 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseSyntax.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/PropertyBaseSyntax.java @@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.properties.handlers.base.PropertyHandler; -import org.skriptlang.skript.lang.properties.PropertyHandler.ReturnablePropertyHandler; +import org.skriptlang.skript.lang.properties.handlers.base.ReturnablePropertyHandler; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java b/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java deleted file mode 100644 index 3cf22acce9a..00000000000 --- a/src/main/java/org/skriptlang/skript/lang/properties/PropertyHandler.java +++ /dev/null @@ -1,329 +0,0 @@ -package org.skriptlang.skript.lang.properties; - -import ch.njol.skript.classes.Changer.ChangeMode; -import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.expressions.ExprSubnodeValue; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.ParseContext; -import ch.njol.skript.lang.parser.ParserInstance; -import ch.njol.skript.registrations.Classes; -import ch.njol.skript.util.StringMode; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.common.properties.conditions.PropCondContains; -import org.skriptlang.skript.common.types.ScriptClassInfo; -import org.skriptlang.skript.lang.converter.Converters; - -import java.util.function.Function; -import java.util.function.Predicate; - -/** - * A handler for a specific property. Any method of resolving or changing the property should be done here. - * A handler can be nearly anything and do nearly anything. Some examples are provided in the sub-interfaces. - *
- * If a handler needs to store state, it should override {@link #newInstance()} to return a new instance of itself. - * Each new instance will be initialized with {@link #init(Expression, ParserInstance)} before use, so state can be - * set up there if it depends on the parser instance or parent expression. - * - * @see ExpressionPropertyHandler - * @param The type of object this property can be applied to. - */ -@ApiStatus.Experimental -public interface PropertyHandler { - - /** - * Creates a new instance of this handler. If a handler does not need to store state, it can simply return {@code this}. - * If a handler needs to store state, it **MUST** return a new instance of itself. See {@link ScriptClassInfo.ScriptNameHandler} - * for an example of a stateful handler. - * - * @return A new instance of this handler, or {@code this} if no state is stored. - */ - default PropertyHandler newInstance() { - return this; - } - - /** - * Initializes this handler with the given parser instance. This method is called once after {@link #newInstance()}. - * If the handler does not need any initialization, it can simply return {@code true}. - *
- * It is safe to print warnings or errors from this method. - * - * @param parentExpression The expression that is using this handler. Can be used to get context about the property usage. - * @param parser The parser instance that will use this handler. - * @return {@code true} if the handler was initialized successfully, {@code false} otherwise. - */ - default boolean init(Expression parentExpression, ParserInstance parser) { - return true; - } - - /** - * A handler to specify that it can return something and what can be returned. - * - * @param The type of object this property can be applied to. - * @param The type of object that is returned by this property. - */ - interface ReturnablePropertyHandler extends PropertyHandler { - - /** - * The return type of this property. This is used for type checking and auto-completion. - * If the property can return multiple types, it should return the most general type that encompasses all - * possible return types. - * - * @return The return type of this property. - */ - @NotNull Class returnType(); - - /** - * The possible return types of this property. This is used for type checking and auto-completion. - * The default implementation returns an array containing the type returned by {@link #returnType()}. - * If the property can return multiple types, it should return all possible return types. - * - * @return The possible return types of this property. - */ - default Class @NotNull [] possibleReturnTypes() { - return new Class[]{ returnType() }; - } - - } - - /** - * A handler that can get and optionally change a property value. This interface is suitable for properties that act - * like expressions, such as "name", "display name", etc. Properties that use this interface should also use - * {@link PropertyBaseExpression} for the parent expression. - * @param The type of object this property can be applied to. - * @param The type of object that is returned by this property. - * - * @see PropertyBaseExpression - */ - @ApiStatus.Experimental - interface ExpressionPropertyHandler extends ReturnablePropertyHandler { - - /** - * Converts the given object to the property value. This method may return arrays if the property is multi-valued. - * - * @param propertyHolder The object to convert. - * @return The property value. - */ - @Nullable ReturnType convert(Type propertyHolder); - - /** - * Returns the types of changes that this property supports. If the property does not support any changes, - * this method should return {@code null}. If the property supports changes, it should return the classes - * that are accepted for each change mode. {@link ChangeMode#RESET} and {@link ChangeMode#DELETE} do not require - * any specific types, so they can return an empty or non-empty array. - *
- * The default implementation returns {@code null}, indicating that the property is read-only. - * - * @param mode The change mode to check. - * @return The types supported by this property for the given change mode, or {@code null} if the property is read-only. - * @see Expression#acceptChange(ChangeMode) - */ - default Class @Nullable [] acceptChange(ChangeMode mode) { - return null; - } - - /** - * Changes the property value of the given object. This method is only called if {@link #acceptChange(ChangeMode)} - * returns a non-null value for the given change mode. - * - * @param propertyHolder The object to change. - * @param delta The new value(s) to set. This is {@code null} for {@link ChangeMode#RESET} and {@link ChangeMode#DELETE}. - * @param mode The change mode to apply. - * @throws UnsupportedOperationException If the property is read-only and does not support changes. - */ - default void change(Type propertyHolder, Object @Nullable [] delta, ChangeMode mode) { - throw new UnsupportedOperationException("Changing is not supported for this property."); - } - - /** - * Whether changing this property requires the source expression to be re-set. - * For example, `set x of (velocity of player) to 1` requires the velocity to be re-set. - * `set name of tool of player` does not, since the slot property updates the item. - * @return Whether the source expression for this property needs to be changed. - */ - default boolean requiresSourceExprChange() { - return false; - } - - /** - * Creates a simple property handler from the given converter function and return type. - * This is a convenience method for creating property handlers that only need to convert - * a value and do not support changing the property or hold any state. - * - * @param converter The function to convert the object to the property value. - * @param returnType The return type of the property. - * @param The type of object this property can be applied to. - * @param The type of object that is returned by this property. - * @return A new property handler that uses the given converter and return type. - */ - @Contract(value = "_, _ -> new", pure = true) - static @NotNull ExpressionPropertyHandler of( - Function converter, - @NotNull Class returnType - ) { - return new ExpressionPropertyHandler<>() { - - @Override - public @Nullable ReturnType convert(Type propertyHolder) { - return converter.apply(propertyHolder); - } - - @Override - public @NotNull Class returnType() { - return returnType; - } - }; - } - } - - @ApiStatus.Experimental - interface TypedValuePropertyHandler extends ExpressionPropertyHandler { - - /** - * @return This thing's value - */ - @Override - @Nullable ValueType convert(Type propertyHolder); - - default Converted convert(Type propertyHolder, ClassInfo expected) { - ValueType value = convert(propertyHolder); - if (value == null) - return null; - return ExprSubnodeValue.convertedValue(value, expected); - } - - /** - * This method can be used to convert change values to ValueType (or null) - * - * @param value The (unchecked) new value - */ - default ValueType convertChangeValue(Object value) throws UnsupportedOperationException { - Class typeClass = returnType(); - ClassInfo classInfo = Classes.getSuperClassInfo(typeClass); - if (value == null) { - return null; - } else if (typeClass == String.class) { - return typeClass.cast(Classes.toString(value, StringMode.MESSAGE)); - } else if (value instanceof String string - && classInfo.getParser() != null - && classInfo.getParser().canParse(ParseContext.CONFIG)) { - return (ValueType) classInfo.getParser().parse(string, ParseContext.CONFIG); - } else { - return Converters.convert(value, typeClass); - } - } - } - - /** - * A handler for a simple property condition. This property must be an inherent condition of the thing, - * and not require secondary inputs, like {@link ContainsHandler} does. Properties that use this interface should a - * lso use {@link PropertyBaseCondition} for the parent condition. - * @param The type of object this property can be applied to. - * - * @see PropertyBaseCondition - */ - @ApiStatus.Experimental - interface ConditionPropertyHandler extends PropertyHandler { - boolean check(Type propertyHolder); - - /** - * Creates a simple property handler from the given predicate. - * - * @param predicate The predicate to evaluate the condition with. - * @param The type of object this property can be applied to. - * @return A new property handler that uses the given predicate. - */ - @Contract(value = "_ -> new", pure = true) - static @NotNull ConditionPropertyHandler of( - Predicate predicate - ) { - return predicate::test; - } - } - - /** - * A handler that can check if a container contains a specific element. - * - * @param The type of object that can contain elements. - * @param The type of object that can be contained. - * - * @see PropCondContains - */ - @ApiStatus.Experimental - interface ContainsHandler extends PropertyHandler { - /** - * Checks if the given container contains the given element. - * - * @param container The container to check. - * @param element The element to check for. - * @return {@code true} if the container contains the element, {@code false} otherwise. - */ - boolean contains(Container container, Element element); - - /** - * The types of elements that can be contained. This is used for type checking and auto-completion. - * Implementations that override {@link #canContain(Class)} may not return accurate results for this method. - * Callers should prefer {@link #canContain(Class)} when possible. - * - * @return The types of elements that can be contained. - */ - Class[] elementTypes(); - - /** - * Checks if this handler can contain the given type of element. - * The default implementation checks if the given type is assignable to any of the types returned by - * {@link #elementTypes()}. - * - * @param type The type to check. - * @return {@code true} if this handler can contain the given type, {@code false} otherwise. - */ - default boolean canContain(Class type) { - for (Class elementType : elementTypes()) { - if (elementType.isAssignableFrom(type)) { - return true; - } - } - return false; - } - } - - /** - * A handler for getting elements/objects contains within another object. - * - * @param The type of object this property can be applied to. - * @param The type of object that is returned by this property. - */ - interface ElementHandler extends ReturnablePropertyHandler { - - /** - * Retrieve an element at the specified {@code index}. - * - * @param type The object that contains elements. - * @param index The index to retrieve an element from. - * @return The retrieved element. - */ - @Nullable ReturnType get(Type type, Integer index); - - /** - * Retrieve elements from {@code start} to {@code end}. - * - * @param type The object that contains elements. - * @param start The starting index. - * @param end The end index. - * @return The retrieved elements. - */ - ReturnType @Nullable [] get(Type type, Integer start, Integer end); - - /** - * Retrieve the number of elements. - * - * @param type The object that contains elements. - * @return The number of elements contained. - */ - int size(Type type); - - } - -} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/handlers/ElementHandler.java b/src/main/java/org/skriptlang/skript/lang/properties/handlers/ElementHandler.java new file mode 100644 index 00000000000..a557a9d35a3 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/handlers/ElementHandler.java @@ -0,0 +1,43 @@ +package org.skriptlang.skript.lang.properties.handlers; + +import org.jetbrains.annotations.ApiStatus.Experimental; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.properties.handlers.base.ReturnablePropertyHandler; + +/** + * A handler for getting elements/objects contains within another object. + * + * @param The type of object this property can be applied to. + * @param The type of object that is returned by this property. + */ +@Experimental +public interface ElementHandler extends ReturnablePropertyHandler { + + /** + * Retrieve an element at the specified {@code index}. + * + * @param type The object that contains elements. + * @param index The index to retrieve an element from. + * @return The retrieved element. + */ + @Nullable ReturnType get(Type type, Integer index); + + /** + * Retrieve elements from {@code start} to {@code end}. + * + * @param type The object that contains elements. + * @param start The starting index. + * @param end The end index. + * @return The retrieved elements. + */ + ReturnType @Nullable [] get(Type type, Integer start, Integer end); + + /** + * Retrieve the number of elements. + * + * @param type The object that contains elements. + * @return The number of elements contained. + */ + int size(Type type); + +} diff --git a/src/main/java/org/skriptlang/skript/lang/properties/handlers/base/ExpressionPropertyHandler.java b/src/main/java/org/skriptlang/skript/lang/properties/handlers/base/ExpressionPropertyHandler.java index 3252fb77ddf..09cfb1b704f 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/handlers/base/ExpressionPropertyHandler.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/handlers/base/ExpressionPropertyHandler.java @@ -21,7 +21,7 @@ * @see PropertyBaseExpression */ @ApiStatus.Experimental -public interface ExpressionPropertyHandler extends PropertyHandler { +public interface ExpressionPropertyHandler extends ReturnablePropertyHandler { /** * Converts the given object to the property value. This method may return arrays if the property is multi-valued. @@ -70,26 +70,6 @@ default boolean requiresSourceExprChange() { return false; } - /** - * The return type of this property. This is used for type checking and auto-completion. - * If the property can return multiple types, it should return the most general type that encompasses all - * possible return types. - * - * @return The return type of this property. - */ - @NotNull Class returnType(); - - /** - * The possible return types of this property. This is used for type checking and auto-completion. - * The default implementation returns an array containing the type returned by {@link #returnType()}. - * If the property can return multiple types, it should return all possible return types. - * - * @return The possible return types of this property. - */ - default Class @NotNull [] possibleReturnTypes() { - return new Class[]{ returnType() }; - } - /** * Creates a simple property handler from the given converter function and return type. * This is a convenience method for creating property handlers that only need to convert diff --git a/src/main/java/org/skriptlang/skript/lang/properties/handlers/base/ReturnablePropertyHandler.java b/src/main/java/org/skriptlang/skript/lang/properties/handlers/base/ReturnablePropertyHandler.java new file mode 100644 index 00000000000..c86fa3ffc4e --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/properties/handlers/base/ReturnablePropertyHandler.java @@ -0,0 +1,35 @@ +package org.skriptlang.skript.lang.properties.handlers.base; + +import org.jetbrains.annotations.ApiStatus.Experimental; +import org.jetbrains.annotations.NotNull; + +/** + * A handler to specify that it can return something and what can be returned. + * + * @param The type of object this property can be applied to. + * @param The type of object that is returned by this property. + */ +@Experimental +public interface ReturnablePropertyHandler extends PropertyHandler { + + /** + * The return type of this property. This is used for type checking and auto-completion. + * If the property can return multiple types, it should return the most general type that encompasses all + * possible return types. + * + * @return The return type of this property. + */ + @NotNull Class returnType(); + + /** + * The possible return types of this property. This is used for type checking and auto-completion. + * The default implementation returns an array containing the type returned by {@link #returnType()}. + * If the property can return multiple types, it should return all possible return types. + * + * @return The possible return types of this property. + */ + default Class @NotNull [] possibleReturnTypes() { + return new Class[]{ returnType() }; + } + +} From 1e7bb0cb945a222bec9a938ce00dcd0afbfcfe06 Mon Sep 17 00:00:00 2001 From: SirSmurfy2 Date: Thu, 1 Jan 2026 16:57:44 -0500 Subject: [PATCH 6/6] Update PropertiesModule.java --- .../skriptlang/skript/common/properties/PropertiesModule.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/skriptlang/skript/common/properties/PropertiesModule.java b/src/main/java/org/skriptlang/skript/common/properties/PropertiesModule.java index 0d3b53028e5..f40c32fcfdf 100644 --- a/src/main/java/org/skriptlang/skript/common/properties/PropertiesModule.java +++ b/src/main/java/org/skriptlang/skript/common/properties/PropertiesModule.java @@ -1,6 +1,7 @@ package org.skriptlang.skript.common.properties; import ch.njol.skript.Skript; +import ch.njol.skript.SkriptConfig; import org.skriptlang.skript.addon.AddonModule; import org.skriptlang.skript.addon.SkriptAddon; import org.skriptlang.skript.common.properties.expressions.PropExprScale;