Skip to content

Commit 469927a

Browse files
authored
Allow addons to use the %classinfo% value property expression. (#7359)
* Add support for more value types. * Use proper array types. * Remove old node value class. * Clean up some bad imports.
1 parent 7012402 commit 469927a

File tree

7 files changed

+273
-125
lines changed

7 files changed

+273
-125
lines changed

src/main/java/ch/njol/skript/ScriptLoader.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,22 @@
99
import ch.njol.skript.lang.SkriptParser;
1010
import ch.njol.skript.lang.Statement;
1111
import ch.njol.skript.lang.TriggerItem;
12-
import ch.njol.skript.lang.TriggerSection;
13-
import ch.njol.skript.lang.function.EffFunctionCall;
1412
import ch.njol.skript.lang.parser.ParserInstance;
15-
import ch.njol.skript.log.*;
16-
import ch.njol.skript.sections.SecLoop;
13+
import ch.njol.skript.log.CountingLogHandler;
14+
import ch.njol.skript.log.LogEntry;
15+
import ch.njol.skript.log.RetainingLogHandler;
16+
import ch.njol.skript.log.SkriptLogger;
1717
import ch.njol.skript.structures.StructOptions.OptionsData;
1818
import ch.njol.skript.test.runner.TestMode;
1919
import ch.njol.skript.util.ExceptionUtils;
2020
import ch.njol.skript.util.SkriptColor;
2121
import ch.njol.skript.util.Task;
2222
import ch.njol.skript.util.Timespan;
2323
import ch.njol.skript.variables.TypeHints;
24-
import ch.njol.util.Kleenean;
2524
import ch.njol.util.NonNullPair;
2625
import ch.njol.util.OpenCloseable;
2726
import ch.njol.util.StringUtils;
2827
import org.bukkit.Bukkit;
29-
import org.bukkit.event.Event;
3028
import org.jetbrains.annotations.ApiStatus;
3129
import org.jetbrains.annotations.Nullable;
3230
import org.skriptlang.skript.lang.script.Script;
@@ -41,11 +39,7 @@
4139
import java.nio.file.Files;
4240
import java.nio.file.Path;
4341
import java.util.*;
44-
import java.util.concurrent.BlockingQueue;
45-
import java.util.concurrent.Callable;
46-
import java.util.concurrent.CompletableFuture;
47-
import java.util.concurrent.LinkedBlockingQueue;
48-
import java.util.concurrent.TimeUnit;
42+
import java.util.concurrent.*;
4943
import java.util.function.Supplier;
5044
import java.util.stream.Collectors;
5145
import java.util.stream.Stream;

src/main/java/ch/njol/skript/classes/data/SkriptClasses.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import ch.njol.skript.lang.util.common.AnyAmount;
1717
import ch.njol.skript.lang.util.common.AnyContains;
1818
import ch.njol.skript.lang.util.common.AnyNamed;
19+
import ch.njol.skript.lang.util.common.AnyValued;
1920
import ch.njol.skript.localization.Noun;
2021
import ch.njol.skript.localization.RegexMessage;
2122
import ch.njol.skript.registrations.Classes;
@@ -899,6 +900,14 @@ public String toVariableNameString(DynamicFunctionReference<?> function) {
899900
.since("2.10")
900901
);
901902

903+
Classes.registerClass(new AnyInfo<>(AnyValued.class, "valued")
904+
.name("Any Valued Thing")
905+
.description("Something that has a value.")
906+
.usage("")
907+
.examples("the text of {node}")
908+
.since("INSERT VERSION")
909+
);
910+
902911
Classes.registerClass(new AnyInfo<>(AnyContains.class, "containing")
903912
.user("any container")
904913
.name("Anything with Contents")

src/main/java/ch/njol/skript/config/EntryNode.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
import java.util.Map.Entry;
55
import java.util.Objects;
66

7+
import ch.njol.skript.lang.util.common.AnyValued;
78
import org.jetbrains.annotations.NotNull;
89
import org.jetbrains.annotations.Nullable;
10+
import org.jetbrains.annotations.UnknownNullability;
911

1012
/**
1113
* @author Peter Güttinger
1214
*/
13-
public class EntryNode extends Node implements Entry<String, String> {
15+
public class EntryNode extends Node implements Entry<String, String>, AnyValued<String> {
1416

1517
private String value;
1618

@@ -35,6 +37,11 @@ public String getValue() {
3537
return value;
3638
}
3739

40+
@Override
41+
public @UnknownNullability String value() {
42+
return this.getValue();
43+
}
44+
3845
@Override
3946
public String setValue(final @Nullable String v) {
4047
if (v == null)
@@ -44,6 +51,21 @@ public String setValue(final @Nullable String v) {
4451
return r;
4552
}
4653

54+
@Override
55+
public void changeValue(String value) throws UnsupportedOperationException {
56+
this.setValue(value);
57+
}
58+
59+
@Override
60+
public Class<String> valueType() {
61+
return String.class;
62+
}
63+
64+
@Override
65+
public boolean supportsValueChange() {
66+
return false; // todo editable configs soon
67+
}
68+
4769
@Override
4870
String save_i() {
4971
return key + config.getSaveSeparator() + value;

src/main/java/ch/njol/skript/expressions/ExprNodeValue.java renamed to src/main/java/ch/njol/skript/expressions/ExprValue.java

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
import ch.njol.skript.Skript;
44
import ch.njol.skript.classes.Changer.ChangeMode;
55
import ch.njol.skript.classes.ClassInfo;
6-
import ch.njol.skript.classes.Parser;
7-
import ch.njol.skript.config.EntryNode;
86
import ch.njol.skript.config.Node;
97
import ch.njol.skript.doc.Description;
108
import ch.njol.skript.doc.Examples;
@@ -14,20 +12,19 @@
1412
import ch.njol.skript.lang.Expression;
1513
import ch.njol.skript.lang.ExpressionType;
1614
import ch.njol.skript.lang.Literal;
17-
import ch.njol.skript.lang.ParseContext;
1815
import ch.njol.skript.lang.SkriptParser.ParseResult;
16+
import ch.njol.skript.lang.util.common.AnyValued;
1917
import ch.njol.skript.registrations.Feature;
2018
import ch.njol.util.Kleenean;
21-
import ch.njol.util.coll.CollectionUtils;
2219
import org.bukkit.event.Event;
2320
import org.jetbrains.annotations.NotNull;
2421
import org.jetbrains.annotations.Nullable;
2522

2623
import java.lang.reflect.Array;
2724

28-
@Name("Value (Experimental)")
25+
@Name("Value")
2926
@Description({
30-
"Returns the value of a node in a loaded config.",
27+
"Returns the value of something that has a value, e.g. a node in a config.",
3128
"The value is automatically converted to the specified type (e.g. text, number) where possible."
3229
})
3330
@Examples({
@@ -44,86 +41,99 @@
4441
# timespan value of {_node} = 12 hours (duration)""",
4542

4643
})
47-
@Since("2.10")
48-
public class ExprNodeValue extends SimplePropertyExpression<Node, Object> {
44+
@Since("2.10 (Nodes), INSERT VERSION (Any)")
45+
public class ExprValue extends SimplePropertyExpression<Object, Object> {
4946

5047
static {
51-
Skript.registerExpression(ExprNodeValue.class, Object.class, ExpressionType.PROPERTY,
52-
"[the] %*classinfo% value [at] %string% (from|in) %node%",
53-
"[the] %*classinfo% value of %node%",
54-
"[the] %*classinfo% values of %nodes%",
55-
"%node%'s %*classinfo% value",
56-
"%nodes%'[s] %*classinfo% values"
48+
Skript.registerExpression(ExprValue.class, Object.class, ExpressionType.PROPERTY,
49+
"[the] %*classinfo% value [at] %string% (from|in) %node%",
50+
"[the] %*classinfo% value of %valued%",
51+
"[the] %*classinfo% values of %valueds%",
52+
"%valued%'s %*classinfo% value",
53+
"%valueds%'[s] %*classinfo% values"
5754
);
5855
}
5956

6057
private boolean isSingle;
6158
private ClassInfo<?> classInfo;
62-
private Parser<?> parser;
6359
private @Nullable Expression<String> pathExpression;
6460

6561
@Override
6662
@SuppressWarnings("unchecked")
6763
public boolean init(Expression<?>[] expressions, int pattern, Kleenean isDelayed, ParseResult parseResult) {
68-
if (!this.getParser().hasExperiment(Feature.SCRIPT_REFLECTION))
69-
return false;
7064
@NotNull Literal<ClassInfo<?>> format;
7165
switch (pattern) {
7266
case 0:
67+
if (!this.getParser().hasExperiment(Feature.SCRIPT_REFLECTION))
68+
return false;
7369
this.isSingle = true;
7470
format = (Literal<ClassInfo<?>>) expressions[0];
7571
this.pathExpression = (Expression<String>) expressions[1];
76-
this.setExpr((Expression<? extends Node>) expressions[2]);
77-
break;
72+
this.setExpr(expressions[2]);
73+
break;
7874
case 1:
7975
this.isSingle = true;
8076
case 2:
8177
format = (Literal<ClassInfo<?>>) expressions[0];
82-
this.setExpr((Expression<? extends Node>) expressions[1]);
78+
this.setExpr(expressions[1]);
8379
break;
8480
case 3:
8581
this.isSingle = true;
8682
default:
8783
format = (Literal<ClassInfo<?>>) expressions[1];
88-
this.setExpr((Expression<? extends Node>) expressions[0]);
84+
this.setExpr(expressions[0]);
8985
}
9086
this.classInfo = format.getSingle();
91-
if (classInfo.getC() == String.class) // don't bother with parser
92-
return true;
93-
this.parser = classInfo.getParser();
94-
if (this.parser == null || !this.parser.canParse(ParseContext.CONFIG)) {
95-
Skript.error("The type '" + classInfo.getName() + "' cannot be used to parse config values.");
96-
return false;
97-
}
9887
return true;
9988
}
10089

10190
@Override
102-
103-
public @Nullable Object convert(@Nullable Node node) {
104-
if (!(node instanceof EntryNode entryNode))
91+
public @Nullable Object convert(@Nullable Object object) {
92+
if (object == null)
10593
return null;
106-
String string = entryNode.getValue();
107-
if (classInfo.getC() == String.class)
108-
return string;
109-
return parser.parse(string, ParseContext.CONFIG);
94+
if (object instanceof AnyValued<?> valued)
95+
return valued.convertedValue(classInfo);
96+
return null;
11097
}
11198

11299
@Override
113-
protected Object[] get(Event event, Node[] source) {
100+
protected Object[] get(Event event, Object[] source) {
114101
if (pathExpression != null) {
102+
if (!(source[0] instanceof Node main))
103+
return (Object[]) Array.newInstance(this.getReturnType(), 0);
115104
String path = pathExpression.getSingle(event);
116-
Node node = source[0].getNodeAt(path);
105+
Node node = main.getNodeAt(path);
117106
Object[] array = (Object[]) Array.newInstance(this.getReturnType(), 1);
118-
array[0] = this.convert(node);
107+
if (!(node instanceof AnyValued<?> valued))
108+
return (Object[]) Array.newInstance(this.getReturnType(), 0);
109+
array[0] = this.convert(valued);
119110
return array;
120111
}
121112
return super.get(source, this);
122113
}
123114

124115
@Override
125116
public Class<?> @Nullable [] acceptChange(ChangeMode mode) {
126-
return null; // todo editable configs in future
117+
if (pathExpression != null) // todo editable configs soon
118+
return null;
119+
return switch (mode) {
120+
case SET -> new Class<?>[] {Object.class};
121+
case RESET, DELETE -> new Class<?>[0];
122+
default -> null;
123+
};
124+
}
125+
126+
@Override
127+
public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
128+
if (pathExpression != null)
129+
return;
130+
Object newValue = delta != null ? delta[0] : null;
131+
for (Object object : getExpr().getArray(event)) {
132+
if (!(object instanceof AnyValued<?> valued))
133+
continue;
134+
if (valued.supportsValueChange())
135+
valued.changeValueSafely(newValue);
136+
}
127137
}
128138

129139
@Override
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package ch.njol.skript.lang.util.common;
2+
3+
import ch.njol.skript.classes.ClassInfo;
4+
import ch.njol.skript.lang.ParseContext;
5+
import ch.njol.skript.registrations.Classes;
6+
import ch.njol.skript.util.StringMode;
7+
import com.sun.jdi.request.StepRequest;
8+
import org.jetbrains.annotations.UnknownNullability;
9+
import org.skriptlang.skript.lang.converter.Converters;
10+
11+
/**
12+
* A provider for anything with a value.
13+
* Anything implementing this (or convertible to this) can be used by the {@link ch.njol.skript.expressions.ExprValue}
14+
* property expression.
15+
*
16+
* @see AnyProvider
17+
*/
18+
public interface AnyValued<Type> extends AnyProvider {
19+
20+
/**
21+
* @return This thing's value
22+
*/
23+
@UnknownNullability
24+
Type value();
25+
26+
default <Converted> Converted convertedValue(ClassInfo<Converted> expected) {
27+
Type value = value();
28+
if (value == null)
29+
return null;
30+
if (expected.getC().isInstance(value))
31+
return expected.getC().cast(value);
32+
33+
// For strings, it is probably better to use toString/Parser in either
34+
// direction, instead of a converter
35+
36+
if (expected.getC() == String.class)
37+
//noinspection unchecked
38+
return (Converted) Classes.toString(value, StringMode.MESSAGE);
39+
if (value instanceof String string
40+
&& expected.getParser() != null
41+
&& expected.getParser().canParse(ParseContext.CONFIG)) {
42+
return expected.getParser().parse(string, ParseContext.CONFIG);
43+
}
44+
45+
return Converters.convert(value, expected.getC());
46+
}
47+
48+
/**
49+
* This is called before {@link #changeValue(Object)}.
50+
* If the result is false, setting the value will never be attempted.
51+
*
52+
* @return Whether this supports being set
53+
*/
54+
default boolean supportsValueChange() {
55+
return false;
56+
}
57+
58+
/**
59+
* The behaviour for changing this thing's value, if possible.
60+
* If not possible, then {@link #supportsValueChange()} should return false and this
61+
* may throw an error.
62+
*
63+
* @param value The new value
64+
* @throws UnsupportedOperationException If this is impossible
65+
*/
66+
default void changeValue(Type value) throws UnsupportedOperationException {
67+
throw new UnsupportedOperationException();
68+
}
69+
70+
/**
71+
*
72+
* @return The type of values this accepts (or provides)
73+
*/
74+
Class<Type> valueType();
75+
76+
/**
77+
* A default implementation of 'resetting' the value (setting it to null).
78+
* Implementations should override this if different behaviour is required.
79+
*
80+
* @throws UnsupportedOperationException If changing is not supported
81+
*/
82+
default void resetValue() throws UnsupportedOperationException {
83+
this.changeValueSafely(null);
84+
}
85+
86+
/**
87+
* This method can be overridden to filter out bad values (e.g. null, objects of the wrong type, etc.)
88+
* and make sure {@link #changeValue(Object)} is not called with a bad parameter.
89+
*
90+
* @param value The (unchecked) new value
91+
*/
92+
default void changeValueSafely(Object value) throws UnsupportedOperationException {
93+
Class<Type> typeClass = this.valueType();
94+
ClassInfo<? super Type> classInfo = Classes.getSuperClassInfo(typeClass);
95+
if (value == null) {
96+
this.changeValue(null);
97+
} else if (typeClass == String.class) {
98+
this.changeValue(typeClass.cast(Classes.toString(value, StringMode.MESSAGE)));
99+
} else if (value instanceof String string
100+
&& classInfo.getParser() != null
101+
&& classInfo.getParser().canParse(ParseContext.CONFIG)) {
102+
Type convert = (Type) classInfo.getParser().parse(string, ParseContext.CONFIG);
103+
this.changeValue(convert);
104+
} else {
105+
Type convert = Converters.convert(value, typeClass);
106+
this.changeValue(convert);
107+
}
108+
}
109+
110+
}

0 commit comments

Comments
 (0)