11package ch .njol .skript .lang ;
22
3- import java .lang .reflect .Array ;
4- import java .util .*;
5- import java .util .Map .Entry ;
6- import java .util .NoSuchElementException ;
7- import java .util .TreeMap ;
8- import java .util .function .Predicate ;
9- import java .util .function .Function ;
10-
113import ch .njol .skript .Skript ;
124import ch .njol .skript .SkriptAPIException ;
135import ch .njol .skript .SkriptConfig ;
146import ch .njol .skript .classes .Changer ;
157import ch .njol .skript .classes .Changer .ChangeMode ;
168import ch .njol .skript .classes .Changer .ChangerUtils ;
179import ch .njol .skript .classes .ClassInfo ;
18- import com .google .common .collect .Iterators ;
19- import org .apache .commons .lang3 .ArrayUtils ;
20- import org .skriptlang .skript .lang .arithmetic .Arithmetics ;
21- import org .skriptlang .skript .lang .arithmetic .OperationInfo ;
22- import org .skriptlang .skript .lang .arithmetic .Operator ;
2310import ch .njol .skript .lang .SkriptParser .ParseResult ;
2411import ch .njol .skript .lang .parser .ParserInstance ;
2512import ch .njol .skript .lang .util .SimpleExpression ;
3421import ch .njol .util .coll .CollectionUtils ;
3522import ch .njol .util .coll .iterator .EmptyIterator ;
3623import ch .njol .util .coll .iterator .SingleItemIterator ;
24+ import com .google .common .collect .Iterators ;
25+ import org .apache .commons .lang3 .ArrayUtils ;
3726import org .bukkit .Bukkit ;
38- import org .bukkit .World ;
3927import org .bukkit .entity .Player ;
4028import org .bukkit .event .Event ;
4129import org .jetbrains .annotations .NotNull ;
4230import org .jetbrains .annotations .Nullable ;
31+ import org .skriptlang .skript .lang .arithmetic .Arithmetics ;
32+ import org .skriptlang .skript .lang .arithmetic .OperationInfo ;
33+ import org .skriptlang .skript .lang .arithmetic .Operator ;
4334import org .skriptlang .skript .lang .comparator .Comparators ;
4435import org .skriptlang .skript .lang .comparator .Relation ;
4536import org .skriptlang .skript .lang .converter .Converters ;
4637import org .skriptlang .skript .lang .script .Script ;
4738import org .skriptlang .skript .lang .script .ScriptWarning ;
4839
40+ import java .lang .reflect .Array ;
41+ import java .util .*;
42+ import java .util .Map .Entry ;
43+ import java .util .function .Function ;
44+ import java .util .function .Predicate ;
45+
4946public class Variable <T > implements Expression <T >, KeyReceiverExpression <T >, KeyProviderExpression <T > {
5047
5148 private final static String SINGLE_SEPARATOR_CHAR = ":" ;
5249 public final static String SEPARATOR = SINGLE_SEPARATOR_CHAR + SINGLE_SEPARATOR_CHAR ;
5350 public final static String LOCAL_VARIABLE_TOKEN = "_" ;
51+ public static final String EPHEMERAL_VARIABLE_TOKEN = "-" ;
5452 private static final char [] reservedTokens = {'~' , '.' , '+' , '$' , '!' , '&' , '^' , '*' };
5553
5654 /**
@@ -67,13 +65,14 @@ public class Variable<T> implements Expression<T>, KeyReceiverExpression<T>, Key
6765 private final Class <? extends T >[] types ;
6866
6967 private final boolean local ;
68+ private final boolean ephemeral ;
7069 private final boolean list ;
7170
7271 private final @ Nullable Variable <?> source ;
7372 private final Map <Event , String []> cache = new WeakHashMap <>();
7473
7574 @ SuppressWarnings ("unchecked" )
76- private Variable (VariableString name , Class <? extends T >[] types , boolean local , boolean list , @ Nullable Variable <?> source ) {
75+ private Variable (VariableString name , Class <? extends T >[] types , boolean local , boolean ephemeral , boolean list , @ Nullable Variable <?> source ) {
7776 assert types .length > 0 ;
7877
7978 assert name .isSimple () || name .getMode () == StringMode .VARIABLE_NAME ;
@@ -83,6 +82,7 @@ private Variable(VariableString name, Class<? extends T>[] types, boolean local,
8382 this .script = parser .isActive () ? parser .getCurrentScript () : null ;
8483
8584 this .local = local ;
85+ this .ephemeral = ephemeral ;
8686 this .list = list ;
8787
8888 this .name = name ;
@@ -160,10 +160,14 @@ else if (character == '%')
160160 }
161161
162162 /**
163- * Prints errors
163+ * Creates a new variable instance with the given name and types. Prints errors.
164+ * @param name The raw name of the variable.
165+ * @param types The types this variable is expected to be.
166+ * @return A new variable instance, or null if the name is invalid or the variable could not be created.
167+ * @param <T> The supertype the variable is expected to be.
164168 */
165169 public static <T > @ Nullable Variable <T > newInstance (String name , Class <? extends T >[] types ) {
166- name = "" + name .trim ();
170+ name = name .trim ();
167171 if (!isValidVariableName (name , true , true ))
168172 return null ;
169173 VariableString variableString = VariableString .newInstance (
@@ -172,17 +176,28 @@ else if (character == '%')
172176 return null ;
173177
174178 boolean isLocal = name .startsWith (LOCAL_VARIABLE_TOKEN );
179+ boolean isEphemeral = name .startsWith (EPHEMERAL_VARIABLE_TOKEN );
175180 boolean isPlural = name .endsWith (SEPARATOR + "*" );
176181
177182 ParserInstance parser = ParserInstance .get ();
178183 Script currentScript = parser .isActive () ? parser .getCurrentScript () : null ;
184+
185+ // check for 'starting with expression' warning
179186 if (currentScript != null
180187 && !SkriptConfig .disableVariableStartingWithExpressionWarnings .value ()
181- && !currentScript .suppressesWarning (ScriptWarning .VARIABLE_STARTS_WITH_EXPRESSION )
182- && (isLocal ? name .substring (LOCAL_VARIABLE_TOKEN .length ()) : name ).startsWith ("%" )) {
183- Skript .warning ("Starting a variable's name with an expression is discouraged ({" + name + "}). " +
184- "You could prefix it with the script's name: " +
185- "{" + StringUtils .substring (currentScript .getConfig ().getFileName (), 0 , -3 ) + SEPARATOR + name + "}" );
188+ && !currentScript .suppressesWarning (ScriptWarning .VARIABLE_STARTS_WITH_EXPRESSION )) {
189+
190+ String strippedName = name ;
191+ if (isLocal ) {
192+ strippedName = strippedName .substring (LOCAL_VARIABLE_TOKEN .length ());
193+ } else if (isEphemeral ) {
194+ strippedName = strippedName .substring (EPHEMERAL_VARIABLE_TOKEN .length ());
195+ }
196+ if (strippedName .startsWith ("%" )) {
197+ Skript .warning ("Starting a variable's name with an expression is discouraged ({" + name + "}). " +
198+ "You could prefix it with the script's name: " +
199+ "{" + StringUtils .substring (currentScript .getConfig ().getFileName (), 0 , -3 ) + SEPARATOR + name + "}" );
200+ }
186201 }
187202
188203 // Check for local variable type hints
@@ -191,7 +206,7 @@ else if (character == '%')
191206 if (!hints .isEmpty ()) { // Type hint(s) available
192207 if (types [0 ] == Object .class ) { // Object is generic, so we initialize with the hints instead
193208 //noinspection unchecked
194- return new Variable <>(variableString , hints .toArray (new Class [0 ]), true , isPlural , null );
209+ return new Variable <>(variableString , hints .toArray (new Class [0 ]), true , isEphemeral , isPlural , null );
195210 }
196211
197212 List <Class <? extends T >> potentialTypes = new ArrayList <>();
@@ -205,7 +220,7 @@ else if (character == '%')
205220 }
206221 if (!potentialTypes .isEmpty ()) { // Hint matches, use variable with exactly correct type
207222 //noinspection unchecked
208- return new Variable <>(variableString , potentialTypes .toArray (Class []::new ), true , isPlural , null );
223+ return new Variable <>(variableString , potentialTypes .toArray (Class []::new ), true , isEphemeral , isPlural , null );
209224 }
210225
211226 // Hint exists and does NOT match any types requested
@@ -223,18 +238,31 @@ else if (character == '%')
223238 }
224239 }
225240
226- return new Variable <>(variableString , types , isLocal , isPlural , null );
241+ return new Variable <>(variableString , types , isLocal , isEphemeral , isPlural , null );
227242 }
228243
229244 @ Override
230245 public boolean init (Expression <?>[] expressions , int matchedPattern , Kleenean isDelayed , ParseResult parseResult ) {
231246 throw new UnsupportedOperationException ();
232247 }
233248
249+ /**
250+ * @return Whether this variable is a local variable, i.e. starts with {@link #LOCAL_VARIABLE_TOKEN}.
251+ */
234252 public boolean isLocal () {
235253 return local ;
236254 }
237255
256+ /**
257+ * @return Whether this variable is an ephemeral variable, i.e. starts with {@link #EPHEMERAL_VARIABLE_TOKEN}.
258+ */
259+ public boolean isEphemeral () {
260+ return ephemeral ;
261+ }
262+
263+ /**
264+ * @return Whether this variable is a list variable, i.e. ends with {@link #SEPARATOR + "*"}.
265+ */
238266 public boolean isList () {
239267 return list ;
240268 }
@@ -296,7 +324,7 @@ public <R> Variable<R> getConvertedExpression(Class<R>... to) {
296324 if (!converterExists ) {
297325 return null ;
298326 }
299- return new Variable <>(name , to , local , list , this );
327+ return new Variable <>(name , to , local , ephemeral , list , this );
300328 }
301329
302330 /**
0 commit comments