diff --git a/src/main/java/ch/njol/skript/expressions/ExprValueWithin.java b/src/main/java/ch/njol/skript/expressions/ExprValueWithin.java index d6c1e73b05a..5cffe752ec1 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprValueWithin.java +++ b/src/main/java/ch/njol/skript/expressions/ExprValueWithin.java @@ -33,6 +33,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.UnparsedLiteral; import ch.njol.skript.registrations.Classes; +import ch.njol.skript.util.ClassInfoReference; import ch.njol.skript.util.Utils; import ch.njol.util.Kleenean; import org.bukkit.event.Event; @@ -69,9 +70,8 @@ public class ExprValueWithin extends WrapperExpression { public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { boolean plural; if (exprs[0] != null) { - UnparsedLiteral unparsedLiteral = (UnparsedLiteral) exprs[0].getSource(); - String input = unparsedLiteral.getData(); - plural = Utils.getEnglishPlural(input).getSecond(); + Literal classInfoReference = (Literal) ClassInfoReference.wrap((Expression>) exprs[0]); + plural = classInfoReference.getSingle().isPlural().isTrue(); } else { plural = parseResult.hasTag("s"); } diff --git a/src/main/java/ch/njol/skript/util/ClassInfoReference.java b/src/main/java/ch/njol/skript/util/ClassInfoReference.java new file mode 100644 index 00000000000..da3eb6b393d --- /dev/null +++ b/src/main/java/ch/njol/skript/util/ClassInfoReference.java @@ -0,0 +1,159 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.util; + +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionList; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.UnparsedLiteral; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.skript.lang.util.SimpleLiteral; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import java.util.Arrays; + +/** + * A ClassInfoReference represents a specific reference to a classinfo including any derivable context + */ +public final class ClassInfoReference { + + @Nullable + private static UnparsedLiteral getSourceUnparsedLiteral(Expression expression) { + while (!(expression instanceof UnparsedLiteral)) { + Expression nextEarliestExpression = expression.getSource(); + if (nextEarliestExpression == expression) { + return null; + } + expression = nextEarliestExpression; + } + return (UnparsedLiteral) expression; + } + + private static Kleenean determineIfPlural(Expression> classInfoExpression) { + UnparsedLiteral sourceUnparsedLiteral = getSourceUnparsedLiteral(classInfoExpression); + if (sourceUnparsedLiteral == null) { + return Kleenean.UNKNOWN; + } + String originalExpr = sourceUnparsedLiteral.getData(); + boolean isPlural = Utils.getEnglishPlural(originalExpr).getSecond(); + return Kleenean.get(isPlural); + } + + /** + * Wraps a ClassInfo expression as a ClassInfoReference expression which will return ClassInfoReferences + * with as much derivable context as possible + * @param classInfoExpression the ClassInfo expression to wrap + * @return a wrapper ClassInfoReference expression + */ + @NonNull + public static Expression wrap(@NonNull Expression> classInfoExpression) { + if (classInfoExpression instanceof ExpressionList) { + ExpressionList classInfoExpressionList = (ExpressionList) classInfoExpression; + Expression[] wrappedExpressions = Arrays.stream(classInfoExpressionList.getExpressions()) + .map(expression -> wrap((Expression>) expression)) + .toArray(Expression[]::new); + return new ExpressionList<>(wrappedExpressions, ClassInfoReference.class, classInfoExpression.getAnd()); + } + Kleenean isPlural = determineIfPlural(classInfoExpression); + if (classInfoExpression instanceof Literal) { + Literal> classInfoLiteral = (Literal>) classInfoExpression; + ClassInfo classInfo = classInfoLiteral.getSingle(); + return new SimpleLiteral<>(new ClassInfoReference(classInfo, isPlural), classInfoLiteral.isDefault()); + } + return new SimpleExpression() { + + @Override + @Nullable + protected ClassInfoReference[] get(Event event) { + if (classInfoExpression.isSingle()) { + ClassInfo classInfo = classInfoExpression.getSingle(event); + if (classInfo == null) { + return new ClassInfoReference[0]; + } + return new ClassInfoReference[] { new ClassInfoReference(classInfo, isPlural) }; + } + return classInfoExpression.stream(event) + .map(ClassInfoReference::new) + .toArray(ClassInfoReference[]::new); + } + + @Override + public boolean isSingle() { + return classInfoExpression.isSingle(); + } + + @Override + public Class getReturnType() { + return ClassInfoReference.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + if (debug) { + return classInfoExpression.toString(event, true) + "(wrapped by " + getClass().getSimpleName() + ")"; + } + return classInfoExpression.toString(event, false); + } + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + return classInfoExpression.init(expressions, matchedPattern, isDelayed, parseResult); + } + }; + } + + private Kleenean plural; + private ClassInfo classInfo; + + public ClassInfoReference(ClassInfo classInfo) { + this(classInfo, Kleenean.UNKNOWN); + } + + public ClassInfoReference(ClassInfo classInfo, Kleenean plural) { + this.classInfo = classInfo; + this.plural = plural; + } + + /** + * @return A Kleenean representing whether this classinfo reference was plural. Kleeanan.UNKNOWN represents + * a reference which did not have appropriate context available. + */ + public Kleenean isPlural() { + return plural; + } + + public void setPlural(Kleenean plural) { + this.plural = plural; + } + + public ClassInfo getClassInfo() { + return classInfo; + } + + public void setClassInfo(ClassInfo classInfo) { + this.classInfo = classInfo; + } + +} diff --git a/src/test/java/org/skriptlang/skript/test/tests/utils/ClassInfoReferenceTest.java b/src/test/java/org/skriptlang/skript/test/tests/utils/ClassInfoReferenceTest.java new file mode 100644 index 00000000000..fade2626c34 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/utils/ClassInfoReferenceTest.java @@ -0,0 +1,103 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package org.skriptlang.skript.test.tests.utils; + +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionList; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.registrations.Classes; +import ch.njol.skript.util.ClassInfoReference; +import ch.njol.skript.variables.Variables; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.Assert; +import org.junit.Test; + + +public class ClassInfoReferenceTest { + + @NonNull + private Expression parseAndWrap(String expr) { + ParseResult parseResult = SkriptParser.parse(expr, "%classinfos%", SkriptParser.ALL_FLAGS, ParseContext.DEFAULT); + if (parseResult == null) + throw new IllegalStateException("Parsed expression " + expr + " is null"); + return ClassInfoReference.wrap((Expression>) parseResult.exprs[0]); + } + + @Test + public void testWrapper() { + ClassInfoReference reference = parseAndWrap("object").getSingle(null); + Assert.assertEquals(Object.class, reference.getClassInfo().getC()); + Assert.assertTrue(reference.isPlural().isFalse()); + + reference = parseAndWrap("string").getSingle(null); + Assert.assertEquals(String.class, reference.getClassInfo().getC()); + Assert.assertTrue(reference.isPlural().isFalse()); + + reference = parseAndWrap("players").getSingle(null); + Assert.assertEquals(Player.class, reference.getClassInfo().getC()); + Assert.assertTrue(reference.isPlural().isTrue()); + + Event event = ContextlessEvent.get(); + Variables.setVariable("classinfo", Classes.getExactClassInfo(Block.class), event, true); + reference = parseAndWrap("{_classinfo}").getSingle(event); + Assert.assertEquals(Block.class, reference.getClassInfo().getC()); + Assert.assertTrue(reference.isPlural().isUnknown()); + + ExpressionList referenceList = (ExpressionList) parseAndWrap("blocks, player or entities"); + Assert.assertFalse(referenceList.getAnd()); + Expression[] childExpressions = referenceList.getExpressions(); + + ClassInfoReference firstReference = childExpressions[0].getSingle(null); + Assert.assertEquals(Block.class, firstReference.getClassInfo().getC()); + Assert.assertTrue(firstReference.isPlural().isTrue()); + + ClassInfoReference secondReference = childExpressions[1].getSingle(null); + Assert.assertEquals(Player.class, secondReference.getClassInfo().getC()); + Assert.assertTrue(secondReference.isPlural().isFalse()); + + ClassInfoReference thirdReference = childExpressions[2].getSingle(null); + Assert.assertEquals(Entity.class, thirdReference.getClassInfo().getC()); + Assert.assertTrue(thirdReference.isPlural().isTrue()); + + referenceList = (ExpressionList) parseAndWrap("{_block} and {_player}"); + Assert.assertTrue(referenceList.getAnd()); + childExpressions = referenceList.getExpressions(); + + event = ContextlessEvent.get(); + Variables.setVariable("block", Classes.getExactClassInfo(Block.class), event, true); + Variables.setVariable("player", Classes.getExactClassInfo(Player.class), event, true); + firstReference = childExpressions[0].getSingle(event); + Assert.assertEquals(Block.class, firstReference.getClassInfo().getC()); + Assert.assertTrue(firstReference.isPlural().isUnknown()); + + secondReference = childExpressions[1].getSingle(event); + Assert.assertEquals(Player.class, secondReference.getClassInfo().getC()); + Assert.assertTrue(secondReference.isPlural().isUnknown()); + + } + +}