Skip to content

Commit 0b6cfc0

Browse files
authored
Fix plural identification for class infos in patterns. (SkriptLang#7304)
* Fix plural issues for class infos. * Remove todo.
1 parent 380492f commit 0b6cfc0

File tree

2 files changed

+148
-28
lines changed

2 files changed

+148
-28
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package ch.njol.skript.test.runner;
2+
3+
import ch.njol.skript.Skript;
4+
import ch.njol.skript.classes.ClassInfo;
5+
import ch.njol.skript.doc.Description;
6+
import ch.njol.skript.doc.Name;
7+
import ch.njol.skript.doc.NoDoc;
8+
import ch.njol.skript.lang.Effect;
9+
import ch.njol.skript.lang.Expression;
10+
import ch.njol.skript.lang.SkriptParser.ParseResult;
11+
import ch.njol.skript.registrations.Classes;
12+
import ch.njol.util.Kleenean;
13+
import org.bukkit.event.Event;
14+
import org.jetbrains.annotations.Nullable;
15+
16+
/**
17+
* This class is used to test whether class-info plurals are detected successfully.
18+
* The syntax in it should never be parsed or used (even in test mode)
19+
* and does nothing.
20+
*/
21+
@Name("Test Plural Class Infos")
22+
@Description("Tests that plural class infos are identified correctly.")
23+
@NoDoc
24+
public class EffTestPluralClassInfos extends Effect {
25+
26+
static {
27+
class Example1 {}
28+
class Example2 {}
29+
class Example3 {}
30+
class Example4 {}
31+
if (TestMode.ENABLED) {
32+
Classes.registerClass(new ClassInfo<>(Example1.class, "testgui")
33+
.user("example1")
34+
.name("Test -ui"));
35+
Classes.registerClass(new ClassInfo<>(Example2.class, "exemplus")
36+
.user("example2")
37+
.name("Test -i"));
38+
Classes.registerClass(new ClassInfo<>(Example3.class, "aardwolf")
39+
.user("example3")
40+
.name("Test -ves"));
41+
Classes.registerClass(new ClassInfo<>(Example4.class, "hoof")
42+
.user("example3")
43+
.name("Test -ves 2"));
44+
Skript.registerEffect(EffTestPluralClassInfos.class,
45+
"classinfo test for %testgui%",
46+
"classinfo test for %testguis%",
47+
"classinfo test for %exemplus%",
48+
"classinfo test for %exempli%",
49+
"classinfo test for %aardwolf%",
50+
"classinfo test for %aardwolves%",
51+
"classinfo test for %hoof%",
52+
"classinfo test for %hooves%");
53+
}
54+
}
55+
56+
@Override
57+
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
58+
return false;
59+
}
60+
61+
@Override
62+
protected void execute(Event event) {
63+
}
64+
65+
@Override
66+
public String toString(@Nullable Event event, boolean debug) {
67+
return "";
68+
}
69+
70+
}

src/main/java/ch/njol/skript/util/Utils.java

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public abstract class Utils {
5252
protected final static Deque<WordEnding> plurals = new LinkedList<>();
5353

5454
static {
55-
plurals.add(new WordEnding("axe", "axes"));
55+
plurals.add(new WordEnding("axe", "axes")); // not complete since we have battleaxe, etc.
5656
plurals.add(new WordEnding("x", "xes"));
5757

5858
plurals.add(new WordEnding("ay", "ays"));
@@ -61,14 +61,20 @@ public abstract class Utils {
6161
plurals.add(new WordEnding("oy", "oys"));
6262
plurals.add(new WordEnding("uy", "uys"));
6363
plurals.add(new WordEnding("kie", "kies"));
64-
plurals.add(new WordEnding("zombie", "zombies"));
64+
plurals.add(new WordEnding("zombie", "zombies", true));
6565
plurals.add(new WordEnding("y", "ies"));
6666

67-
plurals.add(new WordEnding("wife", "wives")); // we have to do the -ife -> ives first
67+
plurals.add(new WordEnding("wife", "wives", true)); // we have to do the -ife -> ives first
6868
plurals.add(new WordEnding("life", "lives"));
69-
plurals.add(new WordEnding("knife", "knives"));
69+
plurals.add(new WordEnding("knife", "knives", true));
7070
plurals.add(new WordEnding("ive", "ives"));
71-
plurals.add(new WordEnding("elf", "elves")); // self shelf elf
71+
72+
plurals.add(new WordEnding("lf", "lves")); // self shelf elf wolf half etc.
73+
plurals.add(new WordEnding("thief", "thieves", true));
74+
plurals.add(new WordEnding("ief", "iefs")); // chiefs, fiefs, briefs
75+
76+
plurals.add(new WordEnding("hoof", "hooves"));
77+
7278
plurals.add(new WordEnding("fe", "ves"));// most -f words' plurals can end in -fs as well as -ves
7379

7480
plurals.add(new WordEnding("h", "hes"));
@@ -79,16 +85,18 @@ public abstract class Utils {
7985
plurals.add(new WordEnding("api", "apis")); // api fix
8086
plurals.add(new WordEnding("us", "i"));
8187

82-
plurals.add(new WordEnding("hoe", "hoes"));
83-
plurals.add(new WordEnding("toe", "toes"));
88+
plurals.add(new WordEnding("hoe", "hoes", true));
89+
plurals.add(new WordEnding("toe", "toes", true));
90+
plurals.add(new WordEnding("foe", "foes", true));
91+
plurals.add(new WordEnding("woe", "woes", true));
8492
plurals.add(new WordEnding("o", "oes"));
8593

86-
plurals.add(new WordEnding("alias", "aliases"));
87-
plurals.add(new WordEnding("gas", "gases"));
94+
plurals.add(new WordEnding("alias", "aliases", true));
95+
plurals.add(new WordEnding("gas", "gases", true));
8896

89-
plurals.add(new WordEnding("child", "children"));
97+
plurals.add(new WordEnding("child", "children")); // grandchild, etc.
9098

91-
plurals.add(new WordEnding("sheep", "sheep"));
99+
plurals.add(new WordEnding("sheep", "sheep", true));
92100

93101
// general ending
94102
plurals.add(new WordEnding("", "s"));
@@ -104,7 +112,7 @@ public static String join(final Object[] objects) {
104112
b.append(", ");
105113
b.append(Classes.toString(objects[i]));
106114
}
107-
return "" + b.toString();
115+
return b.toString();
108116
}
109117

110118
public static String join(final Iterable<?> objects) {
@@ -118,7 +126,7 @@ public static String join(final Iterable<?> objects) {
118126
first = false;
119127
b.append(Classes.toString(o));
120128
}
121-
return "" + b.toString();
129+
return b.toString();
122130
}
123131

124132
@SuppressWarnings("unchecked")
@@ -134,7 +142,7 @@ public static Pair<String, Integer> getAmount(String s) {
134142
} else if (s.matches("an? .+")) {
135143
return new Pair<>(s.split(" ", 2)[1], 1);
136144
}
137-
return new Pair<>(s, Integer.valueOf(-1));
145+
return new Pair<>(s, -1);
138146
}
139147

140148
// public final static class AmountResponse {
@@ -267,14 +275,55 @@ public static File getFile(Plugin plugin) {
267275
public static NonNullPair<String, Boolean> getEnglishPlural(String word) {
268276
assert word != null;
269277
if (word.isEmpty())
270-
return new NonNullPair<>("", Boolean.FALSE);
278+
return new NonNullPair<>("", false);
279+
if (!couldBeSingular(word)) {
280+
for (final WordEnding ending : plurals) {
281+
if (ending.isCompleteWord()) {
282+
// Complete words shouldn't be used as partial pieces
283+
if (word.length() != ending.plural().length())
284+
continue;
285+
}
286+
if (word.endsWith(ending.plural()))
287+
return new NonNullPair<>(
288+
word.substring(0, word.length() - ending.plural().length()) + ending.singular(),
289+
true
290+
);
291+
if (word.endsWith(ending.plural().toUpperCase(Locale.ENGLISH)))
292+
return new NonNullPair<>(
293+
word.substring(0, word.length() - ending.plural().length())
294+
+ ending.singular().toUpperCase(Locale.ENGLISH),
295+
true
296+
);
297+
}
298+
}
299+
return new NonNullPair<>(word, false);
300+
}
301+
302+
private static boolean couldBeSingular(String word) {
271303
for (final WordEnding ending : plurals) {
272-
if (word.endsWith(ending.plural()))
273-
return new NonNullPair<>(word.substring(0, word.length() - ending.plural().length()) + ending.singular(), Boolean.TRUE);
274-
if (word.endsWith(ending.plural().toUpperCase(Locale.ENGLISH)))
275-
return new NonNullPair<>(word.substring(0, word.length() - ending.plural().length()) + ending.singular().toUpperCase(Locale.ENGLISH), Boolean.TRUE);
304+
if (ending.singular().isBlank())
305+
continue;
306+
if (ending.isCompleteWord() && ending.singular().length() != word.length())
307+
continue; // Skip complete words
308+
309+
if (word.endsWith(ending.singular()) || word.toLowerCase().endsWith(ending.singular())) {
310+
return true;
311+
}
276312
}
277-
return new NonNullPair<>(word, Boolean.FALSE);
313+
return false;
314+
}
315+
316+
/**
317+
* Adds a singular/plural word override for the given words.
318+
* This is inserted first in the list of words to be checked: it will always be matched
319+
* and will override all other plurality rules.
320+
* This will only match the word <s>exactly</s>, and will not apply to derivations of the word.
321+
*
322+
* @param singular The singular form of the word
323+
* @param plural The plural form of the word
324+
*/
325+
public static void addPluralOverride(String singular, String plural) {
326+
Utils.plurals.addFirst(new WordEnding(singular, plural, true));
278327
}
279328

280329
/**
@@ -286,6 +335,11 @@ public static NonNullPair<String, Boolean> getEnglishPlural(String word) {
286335
public static String toEnglishPlural(String word) {
287336
assert word != null && word.length() != 0;
288337
for (WordEnding ending : plurals) {
338+
if (ending.isCompleteWord()) {
339+
// Complete words shouldn't be used as partial pieces
340+
if (word.length() != ending.singular().length())
341+
continue;
342+
}
289343
if (word.endsWith(ending.singular()))
290344
return word.substring(0, word.length() - ending.singular().length()) + ending.plural();
291345
}
@@ -791,13 +845,10 @@ public static boolean isInteger(Number... numbers) {
791845
return true;
792846
}
793847

794-
protected static class WordEnding { // To be a record in 2.10
795-
796-
private final String singular, plural;
848+
protected record WordEnding(String singular, String plural, boolean isCompleteWord) {
797849

798-
private WordEnding(String singular, String plural) {
799-
this.singular = singular;
800-
this.plural = plural;
850+
public WordEnding(String singular, String plural) {
851+
this(singular, plural, false);
801852
}
802853

803854
public String singular() {
@@ -811,8 +862,7 @@ public String plural() {
811862
@Override
812863
public boolean equals(Object object) {
813864
if (this == object) return true;
814-
if (!(object instanceof WordEnding)) return false;
815-
WordEnding ending = (WordEnding) object;
865+
if (!(object instanceof WordEnding ending)) return false;
816866
return Objects.equals(singular, ending.singular) && Objects.equals(plural, ending.plural);
817867
}
818868

0 commit comments

Comments
 (0)