Skip to content

Commit 3a8cac8

Browse files
committed
OPENNLP-1886: Pin factory state and supplementary input
1 parent 6a92b30 commit 3a8cac8

11 files changed

Lines changed: 60 additions & 4 deletions

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FinnishLightStemmer.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
@ThreadSafe
7979
public final class FinnishLightStemmer extends AbstractCharArrayStemmer
8080
implements StemmerFactory {
81+
82+
private static final String LLA_SUFFIX = "lla";
83+
8184
/** {@inheritDoc} */
8285
@Override
8386
public Stemmer newStemmer() {
@@ -108,6 +111,7 @@ int stem(char[] s, int len) {
108111
return len;
109112
}
110113

114+
/** Applies the first suffix-removal pass and returns the remaining length. */
111115
private int step1(char[] s, int len) {
112116
if (len > 8) {
113117
if (endsWith(s, len, "kin")) return step1(s, len - 3);
@@ -121,9 +125,10 @@ private int step1(char[] s, int len) {
121125
return len;
122126
}
123127

128+
/** Applies the second suffix-removal pass and returns the remaining length. */
124129
private int step2(char[] s, int len) {
125130
if (len > 5) {
126-
if (endsWith(s, len, "lla") || endsWith(s, len, "tse") || endsWith(s, len, "sti"))
131+
if (endsWith(s, len, LLA_SUFFIX) || endsWith(s, len, "tse") || endsWith(s, len, "sti"))
127132
return len - 3;
128133

129134
if (endsWith(s, len, "ni")) return len - 2;
@@ -134,6 +139,7 @@ private int step2(char[] s, int len) {
134139
return len;
135140
}
136141

142+
/** Applies the final suffix-removal pass and returns the remaining length. */
137143
private int step3(char[] s, int len) {
138144
if (len > 8) {
139145
if (endsWith(s, len, "nnen")) {
@@ -172,7 +178,7 @@ private int step3(char[] s, int len) {
172178

173179
if (endsWith(s, len, "ssa")
174180
|| endsWith(s, len, "sta")
175-
|| endsWith(s, len, "lla")
181+
|| endsWith(s, len, LLA_SUFFIX)
176182
|| endsWith(s, len, "lta")
177183
|| endsWith(s, len, "tta")
178184
|| endsWith(s, len, "ksi")
@@ -198,6 +204,7 @@ private int step3(char[] s, int len) {
198204
return len;
199205
}
200206

207+
/** Applies the first terminal-character normalization and returns the remaining length. */
201208
private int norm1(char[] s, int len) {
202209
if (len > 5 && endsWith(s, len, "hde")) {
203210
s[len - 3] = 'k';
@@ -223,6 +230,7 @@ private int norm1(char[] s, int len) {
223230
return len;
224231
}
225232

233+
/** Applies the final terminal-character normalization and returns the remaining length. */
226234
private int norm2(char[] s, int len) {
227235
if (len > 8) {
228236
if (s[len - 1] == 'e' || s[len - 1] == 'o' || s[len - 1] == 'u') len--;

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchLightStemmer.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@
7777
@ThreadSafe
7878
public final class FrenchLightStemmer extends AbstractCharArrayStemmer
7979
implements StemmerFactory {
80+
81+
private static final String IVE_SUFFIX = "ive";
82+
8083
/** {@inheritDoc} */
8184
@Override
8285
public Stemmer newStemmer() {
@@ -109,7 +112,7 @@ int stem(char[] s, int len) {
109112

110113
if (len > 6 && endsWith(s, len, "ement")) {
111114
len -= 4;
112-
if (len > 3 && endsWith(s, len, "ive")) {
115+
if (len > 3 && endsWith(s, len, IVE_SUFFIX)) {
113116
len--;
114117
s[len - 1] = 'f';
115118
}
@@ -191,7 +194,7 @@ int stem(char[] s, int len) {
191194
return norm(s, len);
192195
}
193196

194-
if (len > 7 && endsWith(s, len, "ive")) {
197+
if (len > 7 && endsWith(s, len, IVE_SUFFIX)) {
195198
len--;
196199
s[len - 1] = 'f';
197200
return norm(s, len);
@@ -233,6 +236,7 @@ int stem(char[] s, int len) {
233236
return norm(s, len);
234237
}
235238

239+
/** Normalizes terminal letters after suffix removal and returns the remaining length. */
236240
private int norm(char[] s, int len) {
237241
if (len > 4) {
238242
for (int i = 0; i < len; i++)

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanLightStemmer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ int stem(char[] s, int len) {
114114
return step2(s, len);
115115
}
116116

117+
/** Returns whether {@code ch} can precede the suffix {@code st}. */
117118
private boolean stEnding(char ch) {
118119
switch (ch) {
119120
case 'b':
@@ -132,6 +133,7 @@ private boolean stEnding(char ch) {
132133
}
133134
}
134135

136+
/** Applies the first suffix-removal pass and returns the remaining length. */
135137
private int step1(char[] s, int len) {
136138
if (len > 5 && s[len - 3] == 'e' && s[len - 2] == 'r' && s[len - 1] == 'n') return len - 3;
137139

@@ -151,6 +153,7 @@ private int step1(char[] s, int len) {
151153
return len;
152154
}
153155

156+
/** Applies the second suffix-removal pass and returns the remaining length. */
154157
private int step2(char[] s, int len) {
155158
if (len > 5 && s[len - 3] == 'e' && s[len - 2] == 's' && s[len - 1] == 't') return len - 3;
156159

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/HungarianLightStemmer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ int stem(char[] s, int len) {
119119
return normalize(s, len);
120120
}
121121

122+
/** Removes case endings and returns the remaining length. */
122123
private int removeCase(char[] s, int len) {
123124
if (len > 6 && endsWith(s, len, "kent")) return len - 4;
124125

@@ -172,6 +173,7 @@ private int removeCase(char[] s, int len) {
172173
return len;
173174
}
174175

176+
/** Removes possessive endings and returns the remaining length. */
175177
private int removePossessive(char[] s, int len) {
176178
if (len > 6) {
177179
if (!isVowel(s[len - 5])
@@ -227,6 +229,7 @@ private int removePossessive(char[] s, int len) {
227229
}
228230

229231
@SuppressWarnings("fallthrough")
232+
/** Removes plural endings and returns the remaining length. */
230233
private int removePlural(char[] s, int len) {
231234
if (len > 3 && s[len - 1] == 'k')
232235
switch (s[len - 2]) {
@@ -240,6 +243,7 @@ private int removePlural(char[] s, int len) {
240243
return len;
241244
}
242245

246+
/** Normalizes terminal vowels and returns the remaining length. */
243247
private int normalize(char[] s, int len) {
244248
if (len > 3)
245249
switch (s[len - 1]) {

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianLightStemmer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public NorwegianLightStemmer(NorwegianVariety first, NorwegianVariety... more) {
9494
useNynorsk = varieties.contains(NorwegianVariety.NYNORSK);
9595
}
9696

97+
/** Initializes a stemmer from validated variety flags. */
9798
private NorwegianLightStemmer(boolean useBokmaal, boolean useNynorsk) {
9899
this.useBokmaal = useBokmaal;
99100
this.useNynorsk = useNynorsk;

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianMinimalStemmer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public NorwegianMinimalStemmer(NorwegianVariety first, NorwegianVariety... more)
9393
useNynorsk = varieties.contains(NorwegianVariety.NYNORSK);
9494
}
9595

96+
/** Initializes a stemmer from validated variety flags. */
9697
private NorwegianMinimalStemmer(boolean useBokmaal, boolean useNynorsk) {
9798
this.useBokmaal = useBokmaal;
9899
this.useNynorsk = useNynorsk;

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/PortugueseLightStemmer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ int stem(char[] s, int len) {
142142
return len;
143143
}
144144

145+
/** Removes a plural suffix and returns the remaining length. */
145146
private int removeSuffix(char[] s, int len) {
146147
if (len > 4 && endsWith(s, len, "es"))
147148
switch (s[len - 3]) {
@@ -192,6 +193,7 @@ private int removeSuffix(char[] s, int len) {
192193
return len;
193194
}
194195

196+
/** Normalizes feminine endings and returns the remaining length. */
195197
private int normFeminine(char[] s, int len) {
196198
if (len > 7
197199
&& (endsWith(s, len, "inha") || endsWith(s, len, "iaca") || endsWith(s, len, "eira"))) {

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/RussianLightStemmer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ int stem(char[] s, int len) {
8989
return normalize(s, len);
9090
}
9191

92+
/** Normalizes terminal characters and returns the remaining length. */
9293
private int normalize(char[] s, int len) {
9394
if (len > 3)
9495
switch (s[len - 1]) {
@@ -101,6 +102,7 @@ private int normalize(char[] s, int len) {
101102
return len;
102103
}
103104

105+
/** Removes case endings and returns the remaining length. */
104106
private int removeCase(char[] s, int len) {
105107
if (len > 6
106108
&& (endsWith(s, len, "\u0438\u044F\u043C\u0438")

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishMinimalStemmer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,12 @@ int stem(char[] s, int len) {
233233
return len;
234234
}
235235

236+
/** Returns whether {@code c} is an unaccented Spanish vowel. */
236237
private boolean isVowel(char c) {
237238
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
238239
}
239240

241+
/** Replaces accented vowels in the stem with their unaccented forms. */
240242
private void removeAccents(char[] s, int len) {
241243
for (int i = 0; i < len; i++) {
242244
switch (s[i]) {

opennlp-core/opennlp-runtime/src/test/java/opennlp/tools/stemmer/light/LightStemmerContractTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import static org.junit.jupiter.api.Assertions.assertEquals;
3434
import static org.junit.jupiter.api.Assertions.assertThrows;
35+
import static org.junit.jupiter.api.Assertions.assertTrue;
3536

3637
/**
3738
* The shared API contract of the light and minimal stemmers: null rejection, identity on empty
@@ -90,6 +91,15 @@ void testDoesNotNormalizeDecomposedInput(Stemmer stemmer) {
9091
assertEquals(decomposed, stemmer.stem(decomposed).toString());
9192
}
9293

94+
@ParameterizedTest
95+
@MethodSource("stemmers")
96+
void testSupplementaryPrefixRemainsIntact(Stemmer stemmer) {
97+
final String result = stemmer.stem("\uD83D\uDE00tests").toString();
98+
assertTrue(result.length() >= 2);
99+
assertTrue(Character.isSurrogatePair(result.charAt(0), result.charAt(1)));
100+
assertEquals(0x1F600, result.codePointAt(0));
101+
}
102+
93103
@ParameterizedTest
94104
@MethodSource("stemmers")
95105
void testConcurrentCallsMatchSerialResults(Stemmer stemmer) throws Exception {

0 commit comments

Comments
 (0)