Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add accepts change condition #94

Open
wants to merge 24 commits into
base: 2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ jobs:
uses: SkriptLang/[email protected]
with:
test_script_directory: src/test/scripts
skript_repo_ref: 2.8.2
skript_repo_ref: dev/patch
extra_plugins_directory: extra-plugins/
118 changes: 118 additions & 0 deletions src/main/java/com/btk5h/skriptmirror/skript/CondAcceptsChange.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.btk5h.skriptmirror.skript;

import ch.njol.skript.Skript;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.lang.Condition;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.util.Patterns;
import ch.njol.util.Kleenean;
import com.btk5h.skriptmirror.util.ClassInfoReference;
import com.btk5h.skriptmirror.util.SkriptUtil;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;


public class CondAcceptsChange extends Condition {

private static final Patterns<ChangeMode> PATTERNS = new Patterns<>(new Object[][] {
{"%classinfo% can be added to %expressions%", ChangeMode.ADD},
{"%classinfo% (can't|cannot) be added to %expressions%", ChangeMode.ADD},
Comment on lines +20 to +21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{"%classinfo% can be added to %expressions%", ChangeMode.ADD},
{"%classinfo% (can't|cannot) be added to %expressions%", ChangeMode.ADD},
{"%classinfo% can be (added|given) to %expressions%", ChangeMode.ADD},
{"%classinfo% (can't|cannot) be (added|given) to %expressions%", ChangeMode.ADD},

Not sure if it's worth doing this, but if you agree, the other modes should be done as well, like clear for DELETE

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds like a good idea. i can't recall if skript has a dirt can be given to player type syntax though. i'll have to double check so we dont accidentally cause a conflict if there is

{"%expressions% can be set to %classinfo%", ChangeMode.SET},
{"%expressions% (can't|cannot) be set to %classinfo%", ChangeMode.SET},
{"%classinfo% can be removed from %expressions%", ChangeMode.REMOVE},
{"%classinfo% (can't|cannot) be removed from %expressions%", ChangeMode.REMOVE},
{"all %classinfo% can be removed from %expressions%", ChangeMode.REMOVE_ALL},
{"all %classinfo% (can't|cannot) be removed from %expressions%", ChangeMode.REMOVE_ALL},
{"%expressions% can be deleted", ChangeMode.DELETE},
{"%expressions% (can't|cannot) be deleted", ChangeMode.DELETE},
{"%expressions% can be reset", ChangeMode.RESET},
{"%expressions% (can't|cannot) be reset", ChangeMode.RESET}
});

static {
Skript.registerCondition(CondAcceptsChange.class, PATTERNS.getPatterns());
}

private ChangeMode desiredChangeMode;
private boolean desiredTypeIsPlural;
private Expression<ClassInfoReference> desiredType;
private Expression<Expression<?>> expressions;

@SuppressWarnings("unchecked")
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
setNegated((matchedPattern % 2) != 0);
desiredChangeMode = PATTERNS.getInfo(matchedPattern);
Expression<?> desiredType = null;
switch (desiredChangeMode) {
case ADD:
case REMOVE:
case REMOVE_ALL:
desiredType = exprs[0];
expressions = (Expression<Expression<?>>) exprs[1];
break;
case SET:
expressions = (Expression<Expression<?>>) exprs[0];
desiredType = exprs[1];
break;
case RESET:
case DELETE:
expressions = (Expression<Expression<?>>) exprs[0];
}
if (desiredType != null) {
this.desiredType = SkriptUtil.wrapClassInfoExpression((Expression<ClassInfo<?>>) desiredType);
}
return SkriptUtil.canInitSafely(desiredType);
}

@Override
public boolean check(Event event) {
if (desiredChangeMode == ChangeMode.DELETE || desiredChangeMode == ChangeMode.RESET)
//noinspection ConstantValue
return expressions.check(event, expressions -> expressions.acceptChange(desiredChangeMode) != null, isNegated());
ClassInfoReference desiredType = this.desiredType.getSingle(event);
if (desiredType == null)
return false;
return expressions.check(event, expression -> acceptsChange(expression, desiredChangeMode, desiredType), isNegated());
}

@Override
public String toString(@Nullable Event event, boolean debug) {
String expressionsString = expressions.toString(event, debug);
String desiredTypesString = desiredType == null ? null : desiredType.toString(event, debug);
switch (desiredChangeMode) {
case ADD:
return desiredTypesString + " can be added to " + expressionsString;
case SET:
return expressionsString + " can be set to " + desiredTypesString;
case RESET:
return expressionsString + " can be reset";
case DELETE:
return expressionsString + " can be deleted";
case REMOVE:
return desiredTypesString + " can be removed from " + expressionsString;
case REMOVE_ALL:
return "all " + desiredTypesString + " can be removed from " + expressionsString;
default:
throw new IllegalStateException();
}
}

private boolean acceptsChange(Expression<?> expression, ChangeMode desiredChangeMode, ClassInfoReference desiredType) {
Class<?>[] acceptableTypes = expression.acceptChange(desiredChangeMode);
//noinspection ConstantValue
if (acceptableTypes != null) {
for (Class<?> acceptableType : acceptableTypes) {
if (acceptableType.isArray()
&& acceptableType.getComponentType().isAssignableFrom(desiredType.getClassInfo().getC())) {
return true;
} else if (desiredType.isPlural() && acceptableType.isAssignableFrom(desiredType.getClassInfo().getC()))
return true;
}
}
return false;
}

}
26 changes: 26 additions & 0 deletions src/main/java/com/btk5h/skriptmirror/skript/Types.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.classes.Parser;
import ch.njol.skript.classes.Serializer;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ParseContext;
import ch.njol.skript.registrations.Classes;
import ch.njol.yggdrasil.Fields;
Expand Down Expand Up @@ -214,6 +215,31 @@ public String getVariableNamePattern() {
Classes.registerClass(new ClassInfo<>(Section.class, "section")
.user("sections?")
);

Classes.registerClass(new ClassInfo<>(Expression.class, "expression")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skript-reflect actually already returns unwrapped Expression objects, but there hasn't been a registered classinfo for it

.user("expressions?")
.parser(new Parser<Expression<?>>() {
@Override
public boolean canParse(ParseContext context) {
return false;
}

@Override
public String getDebugMessage(Expression<?> expression) {
return expression.toString(null, true);
}

@Override
public String toString(Expression<?> expression, int i) {
return expression.toString(null, false);
}

@Override
public String toVariableNameString(Expression<?> expression) {
return toString(expression, 0);
}
}));

}

}
51 changes: 51 additions & 0 deletions src/main/java/com/btk5h/skriptmirror/util/ClassInfoReference.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.btk5h.skriptmirror.util;

import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.expressions.base.WrapperExpression;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;

import java.util.Objects;

public class ClassInfoReference {

private ClassInfo<?> classInfo;
private boolean plural;
private boolean specific;

/**
* Creates a non-specific ClassInfoReference
* @param classInfo the classinfo referenced
*/
public ClassInfoReference(ClassInfo<?> classInfo) {
this.classInfo = classInfo;
}

/**
* Creates a specific ClassInfoReference
* @param classInfo the classinfo referenced
* @param plural whether the reference to the classinfo is plural
*/
public ClassInfoReference(ClassInfo<?> classInfo, boolean plural) {
this.classInfo = classInfo;
this.plural = plural;
this.specific = true;
}

public ClassInfo<?> getClassInfo() {
return classInfo;
}

public boolean isSpecific() {
return specific;
}

public boolean isPlural() {
return plural;
}

}
90 changes: 88 additions & 2 deletions src/main/java/com/btk5h/skriptmirror/util/SkriptUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@
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.TriggerItem;
import ch.njol.skript.lang.UnparsedLiteral;
import ch.njol.skript.lang.parser.ParserInstance;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.log.RetainingLogHandler;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.skript.registrations.Classes;
import ch.njol.skript.registrations.DefaultClasses;
import ch.njol.skript.util.Utils;
import ch.njol.util.Kleenean;
import ch.njol.util.NonNullPair;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.skriptlang.skript.lang.script.Script;

import java.io.File;
Expand Down Expand Up @@ -120,6 +124,23 @@ public static Script getCurrentScript() {
.orElse(null);
}

/**
* Gets the UnparsedLiteral an expression was converted from
* @param expression the expression to get the UnparsedLiteral of
* @return the source UnparsedLiteral or null if there is no such source
*/
public static UnparsedLiteral getSourceUnparsedLiteral(Expression<?> expression) {
Expression<?> sourceExpression = expression.getSource();
while (!(sourceExpression instanceof UnparsedLiteral)) {
Expression<?> nextSourceExpression = sourceExpression.getSource();
if (nextSourceExpression == sourceExpression) {
return null;
}
sourceExpression = nextSourceExpression;
}
return (UnparsedLiteral) sourceExpression;
}

/**
* Gets the {@link ClassInfo} by first converting the given string to a singular.
* Returns {@code Object.class}'s if no {@link ClassInfo} can be found for the given type.
Expand All @@ -143,7 +164,7 @@ public static ClassInfo<?> getUserClassInfo(String name) {
}

/**
* @return the pair of the {@link ClassInfo} in the given string, and whether is is singular.
* @return the pair of the {@link ClassInfo} in the given string, and whether it is singular.
*/
public static NonNullPair<ClassInfo<?>, Boolean> getUserClassInfoAndPlural(String name) {
NonNullPair<String, Boolean> wordData = Utils.getEnglishPlural(name);
Expand All @@ -152,6 +173,71 @@ public static NonNullPair<ClassInfo<?>, Boolean> getUserClassInfoAndPlural(Strin
return new NonNullPair<>(ci, wordData.getSecond());
}

/**
* Wraps a ClassInfo expression into a ClassInfoReference expression
* If possible, specific attributes of ClassInfoReference may be filled in.
* @param sourceExpression the ClassInfo expression
* @return the ClassInfoReference expression
*/
public static Expression<ClassInfoReference> wrapClassInfoExpression(Expression<ClassInfo<?>> sourceExpression) {
ClassInfoReference parsedReference = null;
if (sourceExpression instanceof Literal) {
UnparsedLiteral sourceUnparsedLiteral = getSourceUnparsedLiteral(sourceExpression);
if (sourceUnparsedLiteral == null) {
return null;
}
boolean plural = Utils.getEnglishPlural(sourceUnparsedLiteral.getData()).getSecond();
ClassInfo<?> classInfo = ((Literal<ClassInfo<?>>) sourceExpression).getSingle();
if (classInfo != null) {
parsedReference = new ClassInfoReference(classInfo, plural);
}
}
ClassInfoReference finalParsedReference = parsedReference;
return new SimpleExpression<ClassInfoReference>() {

@Override
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
return sourceExpression.init(expressions, matchedPattern, isDelayed, parseResult);
}

@Override
protected ClassInfoReference[] get(Event event) {
if (finalParsedReference != null) {
return new ClassInfoReference[] { finalParsedReference };
} else if (isSingle()) {
ClassInfo<?> classInfo = sourceExpression.getSingle(event);
if (classInfo == null) {
return new ClassInfoReference[0];
}
return new ClassInfoReference[] { new ClassInfoReference(classInfo) };
} else {
return sourceExpression.stream(event)
.filter(Objects::nonNull)
.map(ClassInfoReference::new)
.toArray(ClassInfoReference[]::new);
}
}

public Class<? extends ClassInfoReference> getReturnType() {
return ClassInfoReference.class;
}

@Override
public boolean isSingle() {
return sourceExpression.isSingle();
}

@Override
public String toString(@Nullable Event event, boolean debug) {
if (debug) {
return sourceExpression.toString(event, true) + " (wrapped by ClassInfoReference)";
}
return sourceExpression.toString(event, false);
}

};
}

/**
* @return the singular form of the given string type,
* converted back to plural if it was plural in the first place.
Expand All @@ -166,7 +252,7 @@ public static String replaceUserInputPatterns(String name) {

/**
* {@return} a {@link Function} to get a {@link Expression}'s value,
* using {@link Expression#getSingle(Event)} if {@link Expression#getSingle(Event)}
* using {@link Expression#getSingle(Event)} if {@link Expression#isSingle()}
* returns {@code true}, otherwise returning {@link Expression#getArray(Event)}.
*/
public static Function<Expression<?>, Object> unwrapWithEvent(Event e) {
Expand Down
33 changes: 33 additions & 0 deletions src/test/scripts/CondChange.sk
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
test "CondChange - set":
assert raw ({_v}) can be set to a string with "a variable should be settable to a string"
assert raw (location of (random player out of all players)) can't be set to a string with "the location expression shouldn't be settable to a string"
assert raw (name of {_v}) can be set to a string with "the name expression should be settable to a string"
assert raw (name of {_v}) can't be set to an integer with "the name expression shouldn't be settable to an integer"
assert raw (name of {_v}) can't be set to strings with "the name expression shouldn't be settable to multiple strings"
assert raw ({_v::*}) can be set to strings with "a list variable should be settable to multiple strings"
assert raw ({_v}) can't be set to strings with "a non-list variable should not be settable to multiple strings"

test "CondChange - reset":
assert raw (uppercase "") can't be reset with "the uppercase expression shouldn't be resettable"
assert raw (portal cooldown of {_v}) can be reset with "the portal cooldown expression should be resettable"

test "CondChange - delete":
assert raw ({_v}) can be deleted with "a variable should be deletable"
assert raw (uppercase "") can't be deleted with "the uppercase expression shouldn't be deletable"

test "CondChange - add":
assert an integer can be added to raw ({_v}) with "an integer should be addable to a variable"
assert integers can be added to raw ({_v}) with "multiple integers should be addable to a variable"
assert a string can't be added to raw (all players) with "a string shouldn't be addable to all players"

test "CondChange - remove":
assert a player can be removed from raw ({_v::*}) with "a player should be removable from a variable"
assert players can be removed from raw ({_v::*}) with "multiple players should be removable from a variable"
assert a player can't be removed from raw (block at {_v}) with "a player shouldn't be removable the block expression"
assert a integer can be removed from raw (durability of {_v}) with "an integer should be removable from the durability expression"

test "CondChange - remove all":
assert all player can be removed from raw ({_v::*}) with "all integer should be removable from a variable"
assert all players can be removed from raw ({_v::*}) with "all integers should be removable from a variable"
assert all strings can't be removed from raw (name of {_v}) with "all strings shouldn't be removable from the name expression"
assert all players can't be removed from raw (name of {_v}) with "all players shouldn't be removable from the name expression"
Loading