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 67d354768a3..00000000000 --- a/src/main/java/ch/njol/skript/expressions/ExprElement.java +++ /dev/null @@ -1,315 +0,0 @@ -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.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 ch.njol.skript.lang.simplification.SimplifiedLiteral; -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/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..935cd77950a --- /dev/null +++ b/src/main/java/org/skriptlang/skript/common/properties/expressions/PropExprElements.java @@ -0,0 +1,310 @@ +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; +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; +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.PropertyMap; + +import java.lang.reflect.Array; +import java.util.Iterator; +import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.BiFunction; +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[][]{ + {"[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, + LAST, + FIRST_X, + LAST_X, + RANDOM, + ORDINAL, + END_ORDINAL, + RANGE; + } + + private ElementsType elementsType; + + 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); + 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.ELEMENT, objects); + if (objects == null) { + objects = defendedObjects; + return LiteralUtils.canInitSafely(objects); + } + + properties = PropertyBaseSyntax.getPossiblePropertyInfos(Property.ELEMENT, objects); + if (properties.isEmpty()) { + return LiteralUtils.canInitSafely(objects); + } + + returnTypes = PropertyBaseSyntax.getPropertyReturnTypes(properties); + if (returnTypes.length == 0) { + returnType = Object.class; + returnTypes = new Class[] {returnType}; + } else { + returnType = Utils.getSuperType(returnTypes); + } + useProperty = true; + + return LiteralUtils.canInitSafely(objects); + } + + @Override + protected Object @Nullable [] get(Event event) { + 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; + } + + 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) { + 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; + } + + /** + * 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) + .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)); + } + + /** + * 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) -> + 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 + 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() { + return Property.ELEMENT; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + 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/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/common/types/QueueClassInfo.java b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java index cd2d2ccf88b..14a2a03105c 100644 --- a/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java +++ b/src/main/java/org/skriptlang/skript/common/types/QueueClassInfo.java @@ -13,6 +13,7 @@ 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.util.SkriptQueue; @@ -48,7 +49,14 @@ 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.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 QueueElementHandler()); } private static class QueueChanger implements Changer { @@ -143,4 +151,32 @@ 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); + } + + @Override + public Object @Nullable [] get(SkriptQueue queue, Integer start, Integer end) { + return queue.removeRangeSafely(start, end); + } + + @Override + public int size(SkriptQueue queue) { + return queue.size(); + } + + @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..c44f59a2af4 100644 --- a/src/main/java/org/skriptlang/skript/lang/properties/Property.java +++ b/src/main/java/org/skriptlang/skript/lang/properties/Property.java @@ -18,6 +18,7 @@ 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.ExpressionPropertyHandler; import org.skriptlang.skript.lang.properties.PropertyHandler.TypedValuePropertyHandler; @@ -244,6 +245,17 @@ public record PropertyInfo>(Property Skript.instance(), TypedValuePropertyHandler.class); + /** + * 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 + ); + /** * Register all Skript's default properties. Should be done prior to loading classinfos. */ @@ -256,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/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); + } + } 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..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,4 +289,41 @@ default boolean canContain(Class type) { } } + /** + * 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); + + } + }