This is an automated email from the ASF dual-hosted git repository. krickert pushed a commit to branch light-stemmers in repository https://gitbox.apache.org/repos/asf/opennlp.git
commit 2d00a837d54b2a26322605ff4ee0964a53fb68df Author: Kristian Rickert <[email protected]> AuthorDate: Thu Jul 16 06:21:14 2026 -0400 OPENNLP-1886: Share the char-array stemming shell, cite the algorithms with verified links, drop the dangling deprecation --- .../stemmer/light/AbstractCharArrayStemmer.java | 52 ++++++++++++++++ .../tools/stemmer/light/EnglishMinimalStemmer.java | 27 +++------ .../tools/stemmer/light/FinnishLightStemmer.java | 45 ++++---------- .../tools/stemmer/light/FrenchLightStemmer.java | 32 ++++------ .../tools/stemmer/light/FrenchMinimalStemmer.java | 30 ++++------ .../tools/stemmer/light/GermanLightStemmer.java | 29 +++------ .../tools/stemmer/light/GermanMinimalStemmer.java | 29 +++------ .../tools/stemmer/light/HungarianLightStemmer.java | 45 ++++---------- .../tools/stemmer/light/ItalianLightStemmer.java | 29 +++------ .../tools/stemmer/light/NorwegianLightStemmer.java | 42 +++++-------- .../stemmer/light/NorwegianMinimalStemmer.java | 41 +++++-------- .../stemmer/light/PortugueseLightStemmer.java | 30 ++++------ .../tools/stemmer/light/RussianLightStemmer.java | 30 ++++------ .../tools/stemmer/light/SpanishLightStemmer.java | 29 +++------ .../tools/stemmer/light/SpanishMinimalStemmer.java | 32 ++++------ .../opennlp/tools/stemmer/light/StemmerUtil.java | 69 +++++----------------- .../tools/stemmer/light/SwedishLightStemmer.java | 30 ++++------ .../tools/stemmer/light/SwedishMinimalStemmer.java | 33 ++++------- .../stemmer/light/LightStemmerParityTest.java | 6 +- 19 files changed, 249 insertions(+), 411 deletions(-) diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/AbstractCharArrayStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/AbstractCharArrayStemmer.java new file mode 100644 index 000000000..16f994c19 --- /dev/null +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/AbstractCharArrayStemmer.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package opennlp.tools.stemmer.light; + +import opennlp.tools.stemmer.Stemmer; + +/** + * Base class of the light and minimal stemmers. It implements the public + * {@link Stemmer#stem(CharSequence)} contract once: the word is validated, copied into a + * {@code char[]} buffer, stemmed in place by the concrete algorithm via {@link #stem(char[], int)}, + * and returned as a {@link String} of the resulting length. + */ +abstract class AbstractCharArrayStemmer implements Stemmer { + + /** {@inheritDoc} */ + @Override + public CharSequence stem(CharSequence word) { + if (word == null) { + throw new IllegalArgumentException("word must not be null"); + } + final int length = word.length(); + final char[] buffer = new char[length]; + for (int i = 0; i < length; i++) { + buffer[i] = word.charAt(i); + } + final int stemmedLength = stem(buffer, length); + return new String(buffer, 0, stemmedLength); + } + + /** + * Stems the buffer prefix in place. + * + * @param s The buffer holding the word; the algorithm may overwrite characters in place. + * @param len The filled length of the buffer. + * @return The length of the stem within {@code s}. + */ + abstract int stem(char[] s, int len); +} diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/EnglishMinimalStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/EnglishMinimalStemmer.java index d1f9bb6f8..500599865 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/EnglishMinimalStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/EnglishMinimalStemmer.java @@ -20,10 +20,13 @@ package opennlp.tools.stemmer.light; import opennlp.tools.commons.ThreadSafe; import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; + /** * Minimal plural stemmer for English. * - * <p>This stemmer implements the "S-Stemmer" from <i>How Effective Is Suffixing?</i> Donna Harman. + * <p>This stemmer implements the "S-Stemmer" from + * <a href="https://doi.org/10.1002/(SICI)1097-4571(199101)42:1%3C7::AID-ASI2%3E3.0.CO;2-P"> + * <i>How Effective Is Suffixing?</i></a> by Donna Harman (JASIS 42(1), 1991). * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -31,30 +34,18 @@ import opennlp.tools.stemmer.StemmerFactory; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class EnglishMinimalStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class EnglishMinimalStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } + /** {@inheritDoc} */ + @Override @SuppressWarnings("fallthrough") - private int stem(char[] s, int len) { + int stem(char[] s, int len) { if (len < 3 || s[len - 1] != 's') return len; switch (s[len - 2]) { diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FinnishLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FinnishLightStemmer.java index b1035ace6..d06e6ff1f 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FinnishLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FinnishLightStemmer.java @@ -57,11 +57,16 @@ import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.delete; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; +import static opennlp.tools.stemmer.light.StemmerUtil.isVowel; + /** * Light Stemmer for Finnish. * - * <p>This stemmer implements the algorithm described in: <i>Report on CLEF-2003 Monolingual - * Tracks</i> Jacques Savoy + * <p>This stemmer implements the algorithm described in: + * <a href="https://ceur-ws.org/Vol-1169/CLEF2003wn-adhoc-Savoy2003b.pdf"><i>Report on CLEF-2003 + * Monolingual Tracks</i></a> by Jacques Savoy (CLEF 2003 working notes). The inline suffix + * literals mirror the suffix tables of the cited paper and are kept inline for fidelity to the + * published algorithm. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -69,29 +74,17 @@ import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class FinnishLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class FinnishLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len < 4) return len; for (int i = 0; i < len; i++) @@ -247,18 +240,4 @@ public final class FinnishLightStemmer implements Stemmer, StemmerFactory { return len; } - - private boolean isVowel(char ch) { - switch (ch) { - case 'a': - case 'e': - case 'i': - case 'o': - case 'u': - case 'y': - return true; - default: - return false; - } - } } diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchLightStemmer.java index 38f11138a..2efea82e7 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchLightStemmer.java @@ -57,11 +57,15 @@ import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.delete; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; + /** * Light Stemmer for French. * - * <p>This stemmer implements the "UniNE" algorithm in: <i>Light Stemming Approaches for the French, - * Portuguese, German and Hungarian Languages</i> Jacques Savoy + * <p>This stemmer implements the "UniNE" algorithm in: + * <a href="https://doi.org/10.1145/1141277.1141523"><i>Light Stemming Approaches for the French, + * Portuguese, German and Hungarian Languages</i></a> by Jacques Savoy (ACM SAC 2006). The inline + * suffix literals mirror the suffix tables of the cited paper and are kept inline for fidelity + * to the published algorithm. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -69,29 +73,17 @@ import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class FrenchLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class FrenchLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len > 5 && s[len - 1] == 'x') { if (s[len - 3] == 'a' && s[len - 2] == 'u' && s[len - 4] != 'e') s[len - 2] = 'l'; len--; @@ -270,6 +262,7 @@ public final class FrenchLightStemmer implements Stemmer, StemmerFactory { char ch = s[0]; for (int i = 1; i < len; i++) { + // Character.isLetter is intentional: it is the letter test of the ported algorithm. if (s[i] == ch && Character.isLetter(ch)) len = delete(s, i--, len); else ch = s[i]; } @@ -281,6 +274,7 @@ public final class FrenchLightStemmer implements Stemmer, StemmerFactory { if (s[len - 1] == 'r') len--; if (s[len - 1] == 'e') len--; if (s[len - 1] == 'e') len--; + // Character.isLetter is intentional: it is the letter test of the ported algorithm. if (s[len - 1] == s[len - 2] && Character.isLetter(s[len - 1])) len--; } return len; diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchMinimalStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchMinimalStemmer.java index 88ddde778..14a593101 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchMinimalStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/FrenchMinimalStemmer.java @@ -54,11 +54,14 @@ package opennlp.tools.stemmer.light; import opennlp.tools.commons.ThreadSafe; import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; + /** * Light Stemmer for French. * - * <p>This stemmer implements the following algorithm: <i>A Stemming procedure and stopword list for - * general French corpora.</i> Jacques Savoy. + * <p>This stemmer implements the following algorithm: + * <a href="https://doi.org/10.1002/(SICI)1097-4571(1999)50:10%3C944::AID-ASI9%3E3.0.CO;2-Q"> + * <i>A Stemming Procedure and Stopword List for General French Corpora</i></a> by Jacques Savoy + * (JASIS 50(10), 1999). * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -66,29 +69,17 @@ import opennlp.tools.stemmer.StemmerFactory; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class FrenchMinimalStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class FrenchMinimalStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len < 6) return len; if (s[len - 1] == 'x') { @@ -100,6 +91,7 @@ public final class FrenchMinimalStemmer implements Stemmer, StemmerFactory { if (s[len - 1] == 'r') len--; if (s[len - 1] == 'e') len--; if (s[len - 1] == 'é') len--; + // Character.isLetter is intentional: it is the letter test of the ported algorithm. if (s[len - 1] == s[len - 2] && Character.isLetter(s[len - 1])) len--; return len; } diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanLightStemmer.java index e1daca454..f0b81f04c 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanLightStemmer.java @@ -54,11 +54,13 @@ package opennlp.tools.stemmer.light; import opennlp.tools.commons.ThreadSafe; import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; + /** * Light Stemmer for German. * - * <p>This stemmer implements the "UniNE" algorithm in: <i>Light Stemming Approaches for the French, - * Portuguese, German and Hungarian Languages</i> Jacques Savoy + * <p>This stemmer implements the "UniNE" algorithm in: + * <a href="https://doi.org/10.1145/1141277.1141523"><i>Light Stemming Approaches for the French, + * Portuguese, German and Hungarian Languages</i></a> by Jacques Savoy (ACM SAC 2006). * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -66,30 +68,17 @@ import opennlp.tools.stemmer.StemmerFactory; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class GermanLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class GermanLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { for (int i = 0; i < len; i++) switch (s[i]) { case 'ä': diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanMinimalStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanMinimalStemmer.java index fe711a2ed..d6c2db31f 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanMinimalStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/GermanMinimalStemmer.java @@ -54,11 +54,13 @@ package opennlp.tools.stemmer.light; import opennlp.tools.commons.ThreadSafe; import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; + /** * Minimal Stemmer for German. * - * <p>This stemmer implements the following algorithm: <i>Morphologie et recherche d'information</i> - * Jacques Savoy. + * <p>This stemmer implements the following algorithm: + * <a href="http://members.unine.ch/jacques.savoy/clef/morpho.pdf"><i>Morphologie et recherche + * d'information</i></a> by Jacques Savoy (technical report). * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -66,30 +68,17 @@ import opennlp.tools.stemmer.StemmerFactory; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class GermanMinimalStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class GermanMinimalStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len < 5) return len; for (int i = 0; i < len; i++) diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/HungarianLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/HungarianLightStemmer.java index cb8fed7c6..ad0b9a962 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/HungarianLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/HungarianLightStemmer.java @@ -56,11 +56,16 @@ import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; +import static opennlp.tools.stemmer.light.StemmerUtil.isVowel; + /** * Light Stemmer for Hungarian. * - * <p>This stemmer implements the "UniNE" algorithm in: <i>Light Stemming Approaches for the French, - * Portuguese, German and Hungarian Languages</i> Jacques Savoy + * <p>This stemmer implements the "UniNE" algorithm in: + * <a href="https://doi.org/10.1145/1141277.1141523"><i>Light Stemming Approaches for the French, + * Portuguese, German and Hungarian Languages</i></a> by Jacques Savoy (ACM SAC 2006). The inline + * suffix literals mirror the suffix tables of the cited paper and are kept inline for fidelity + * to the published algorithm. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -68,29 +73,17 @@ import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class HungarianLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class HungarianLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { for (int i = 0; i < len; i++) switch (s[i]) { case 'á': @@ -256,18 +249,4 @@ public final class HungarianLightStemmer implements Stemmer, StemmerFactory { } return len; } - - private boolean isVowel(char ch) { - switch (ch) { - case 'a': - case 'e': - case 'i': - case 'o': - case 'u': - case 'y': - return true; - default: - return false; - } - } } diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/ItalianLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/ItalianLightStemmer.java index 2664775df..23bee8ed2 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/ItalianLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/ItalianLightStemmer.java @@ -54,11 +54,13 @@ package opennlp.tools.stemmer.light; import opennlp.tools.commons.ThreadSafe; import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; + /** * Light Stemmer for Italian. * - * <p>This stemmer implements the algorithm described in: <i>Report on CLEF-2001 Experiments</i> - * Jacques Savoy + * <p>This stemmer implements the algorithm described in: + * <a href="https://doi.org/10.1007/3-540-45691-0_3"><i>Report on CLEF-2001 Experiments</i></a> + * by Jacques Savoy (CLEF 2001, LNCS 2406). * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -66,30 +68,17 @@ import opennlp.tools.stemmer.StemmerFactory; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class ItalianLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class ItalianLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len < 6) return len; for (int i = 0; i < len; i++) diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianLightStemmer.java index 4caeb4815..51b49a483 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianLightStemmer.java @@ -56,40 +56,22 @@ import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; + /** * Light Stemmer for Norwegian. * - * <p>Parts of this stemmer is adapted from SwedishLightStemFilter, except that while the Swedish - * one has a pre-defined rule set and a corresponding corpus to validate against whereas the + * <p>Parts of this stemmer are adapted from the {@link SwedishLightStemmer}; while the Swedish + * algorithm has a pre-defined rule set and a corresponding corpus to validate against, the * Norwegian one is hand crafted. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. - * Instances are stateless and safe for concurrent use by multiple threads; each instance is also + * Instances are immutable and safe for concurrent use by multiple threads; each instance is also * its own {@link StemmerFactory}. Input is expected to be lowercase, as produced by a * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class NorwegianLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - - /** {@inheritDoc} */ - @Override - public Stemmer newStemmer() { - return this; - } +public final class NorwegianLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { private final boolean useBokmaal; private final boolean useNynorsk; @@ -118,8 +100,16 @@ public final class NorwegianLightStemmer implements Stemmer, StemmerFactory { useNynorsk = nynorsk; } - private int stem(char[] s, int len) { - // Remove posessive -s (bilens -> bilen) and continue checking + /** {@inheritDoc} */ + @Override + public Stemmer newStemmer() { + return this; + } + + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { + // Remove possessive -s (bilens -> bilen) and continue checking if (len > 4 && s[len - 1] == 's') len--; // Remove common endings, single-pass diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianMinimalStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianMinimalStemmer.java index e079491fd..766099df7 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianMinimalStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/NorwegianMinimalStemmer.java @@ -58,37 +58,18 @@ import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; /** - * Minimal Stemmer for Norwegian Bokmål (no-nb) and Nynorsk (no-nn) + * Minimal Stemmer for Norwegian Bokmål ({@code nb}) and Nynorsk ({@code nn}). * - * <p>Stems known plural forms for Norwegian nouns only, together with genitiv -s + * <p>Stems known plural forms for Norwegian nouns only, together with the genitive {@code -s}. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. - * Instances are stateless and safe for concurrent use by multiple threads; each instance is also + * Instances are immutable and safe for concurrent use by multiple threads; each instance is also * its own {@link StemmerFactory}. Input is expected to be lowercase, as produced by a * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class NorwegianMinimalStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - - /** {@inheritDoc} */ - @Override - public Stemmer newStemmer() { - return this; - } +public final class NorwegianMinimalStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { private final boolean useBokmaal; private final boolean useNynorsk; @@ -117,8 +98,16 @@ public final class NorwegianMinimalStemmer implements Stemmer, StemmerFactory { useNynorsk = nynorsk; } - private int stem(char[] s, int len) { - // Remove genitiv s + /** {@inheritDoc} */ + @Override + public Stemmer newStemmer() { + return this; + } + + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { + // Remove genitive s if (len > 4 && s[len - 1] == 's') len--; if (len > 5 diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/PortugueseLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/PortugueseLightStemmer.java index bf85bbb26..ba155ce75 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/PortugueseLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/PortugueseLightStemmer.java @@ -56,11 +56,15 @@ import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; + /** * Light Stemmer for Portuguese * - * <p>This stemmer implements the "UniNE" algorithm in: <i>Light Stemming Approaches for the French, - * Portuguese, German and Hungarian Languages</i> Jacques Savoy + * <p>This stemmer implements the "UniNE" algorithm in: + * <a href="https://doi.org/10.1145/1141277.1141523"><i>Light Stemming Approaches for the French, + * Portuguese, German and Hungarian Languages</i></a> by Jacques Savoy (ACM SAC 2006). The inline + * suffix literals mirror the suffix tables of the cited paper and are kept inline for fidelity + * to the published algorithm. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -68,29 +72,17 @@ import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class PortugueseLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class PortugueseLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len < 4) return len; len = removeSuffix(s, len); diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/RussianLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/RussianLightStemmer.java index 2089b00c2..2e9e2a55d 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/RussianLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/RussianLightStemmer.java @@ -56,11 +56,15 @@ import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; + /** * Light Stemmer for Russian. * - * <p>This stemmer implements the following algorithm: <i>Indexing and Searching Strategies for the - * Russian Language.</i> Ljiljana Dolamic and Jacques Savoy. + * <p>This stemmer implements the following algorithm: + * <a href="https://doi.org/10.1002/asi.21191"><i>Indexing and Searching Strategies for the + * Russian Language</i></a> by Ljiljana Dolamic and Jacques Savoy (JASIST 60(12), 2009). The + * inline suffix literals mirror the suffix tables of the cited paper and are kept inline for + * fidelity to the published algorithm. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -68,29 +72,17 @@ import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class RussianLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class RussianLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { len = removeCase(s, len); return normalize(s, len); } diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishLightStemmer.java index 0645593da..bcde8199c 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishLightStemmer.java @@ -54,11 +54,13 @@ package opennlp.tools.stemmer.light; import opennlp.tools.commons.ThreadSafe; import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; + /** * Light Stemmer for Spanish * - * <p>This stemmer implements the algorithm described in: <i>Report on CLEF-2001 Experiments</i> - * Jacques Savoy + * <p>This stemmer implements the algorithm described in: + * <a href="https://doi.org/10.1007/3-540-45691-0_3"><i>Report on CLEF-2001 Experiments</i></a> + * by Jacques Savoy (CLEF 2001, LNCS 2406). * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -66,30 +68,17 @@ import opennlp.tools.stemmer.StemmerFactory; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class SpanishLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class SpanishLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len < 5) return len; for (int i = 0; i < len; i++) diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishMinimalStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishMinimalStemmer.java index b14ad4996..0a4c94a5e 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishMinimalStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SpanishMinimalStemmer.java @@ -20,39 +20,29 @@ package opennlp.tools.stemmer.light; import opennlp.tools.commons.ThreadSafe; import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; + /** * Minimal plural stemmer for Spanish. * - * <p>This stemmer implements the "plurals" stemmer for spanish language. + * <p>This stemmer implements the "plurals" stemmer for the Spanish language. * - * @deprecated Use {@link SpanishPluralStemmer} instead. + * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. + * Instances are stateless and safe for concurrent use by multiple threads; each instance is also + * its own {@link StemmerFactory}. Input is expected to be lowercase, as produced by a + * case-folding normalization step; the stemmer does not fold case itself.</p> */ -@Deprecated @ThreadSafe -public final class SpanishMinimalStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class SpanishMinimalStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len < 4 || s[len - 1] != 's') return len; for (int i = 0; i < len; i++) diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/StemmerUtil.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/StemmerUtil.java index 4c0b3f804..8745edc27 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/StemmerUtil.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/StemmerUtil.java @@ -26,27 +26,6 @@ final class StemmerUtil { private StemmerUtil() { } - /** - * Checks whether the buffer starts with a prefix. - * - * @param s The input buffer. - * @param len The filled length of the buffer. - * @param prefix The prefix to test. - * @return {@code true} if the buffer starts with {@code prefix}. - */ - static boolean startsWith(char[] s, int len, String prefix) { - final int prefixLen = prefix.length(); - if (prefixLen > len) { - return false; - } - for (int i = 0; i < prefixLen; i++) { - if (s[i] != prefix.charAt(i)) { - return false; - } - } - return true; - } - /** * Checks whether the buffer ends with a suffix. * @@ -68,27 +47,6 @@ final class StemmerUtil { return true; } - /** - * Checks whether the buffer ends with a suffix. - * - * @param s The input buffer. - * @param len The filled length of the buffer. - * @param suffix The suffix to test. - * @return {@code true} if the buffer ends with {@code suffix}. - */ - static boolean endsWith(char[] s, int len, char[] suffix) { - final int suffixLen = suffix.length; - if (suffixLen > len) { - return false; - } - for (int i = suffixLen - 1; i >= 0; i--) { - if (s[len - (suffixLen - i)] != suffix[i]) { - return false; - } - } - return true; - } - /** * Deletes one character in place. * @@ -105,19 +63,24 @@ final class StemmerUtil { } /** - * Deletes {@code nChars} characters in place. + * Checks whether a character is one of the unaccented vowel letters {@code a}, {@code e}, + * {@code i}, {@code o}, {@code u} or {@code y}. This is the vowel test shared by the Finnish + * and Hungarian light stemming algorithms. * - * @param s The input buffer. - * @param pos The position of the first character to delete. - * @param len The filled length of the buffer. - * @param nChars The number of characters to delete; {@code pos + nChars} must not exceed - * {@code len}. - * @return The filled length after the deletion. + * @param ch The character to test. + * @return {@code true} if {@code ch} is one of the six vowel letters. */ - static int deleteN(char[] s, int pos, int len, int nChars) { - if (pos + nChars < len) { - System.arraycopy(s, pos + nChars, s, pos, len - pos - nChars); + static boolean isVowel(char ch) { + switch (ch) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + case 'y': + return true; + default: + return false; } - return len - nChars; } } diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SwedishLightStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SwedishLightStemmer.java index 8f835c1a2..eda81fe45 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SwedishLightStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SwedishLightStemmer.java @@ -56,11 +56,15 @@ import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; + /** * Light Stemmer for Swedish. * - * <p>This stemmer implements the algorithm described in: <i>Report on CLEF-2003 Monolingual - * Tracks</i> Jacques Savoy + * <p>This stemmer implements the algorithm described in: + * <a href="https://ceur-ws.org/Vol-1169/CLEF2003wn-adhoc-Savoy2003b.pdf"><i>Report on CLEF-2003 + * Monolingual Tracks</i></a> by Jacques Savoy (CLEF 2003 working notes). The inline suffix + * literals mirror the suffix tables of the cited paper and are kept inline for fidelity to the + * published algorithm. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -68,29 +72,17 @@ import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class SwedishLightStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class SwedishLightStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len > 4 && s[len - 1] == 's') len--; if (len > 7 && (endsWith(s, len, "elser") || endsWith(s, len, "heten"))) return len - 5; diff --git a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SwedishMinimalStemmer.java b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SwedishMinimalStemmer.java index 1eb2f6454..f2b07ceee 100644 --- a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SwedishMinimalStemmer.java +++ b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/stemmer/light/SwedishMinimalStemmer.java @@ -55,13 +55,12 @@ import opennlp.tools.stemmer.Stemmer; import opennlp.tools.stemmer.StemmerFactory; import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; + /** - * Minimal Stemmer for Swedish. The algorithm is an adapted version of the SwedishLightStemmer, but - * only stripping the most common plural suffixes for nouns: -ar/arne/arna/aren, -at, -er/erna, -et, - * -or/orna, -en. We do not strip -an or -ans suffixes, since that would require a large dictionary - * of exceptions. - * - * @since 9.0.0 + * Minimal Stemmer for Swedish. The algorithm is an adapted version of the + * {@link SwedishLightStemmer}, but only stripping the most common plural suffixes for nouns: + * -ar/arne/arna/aren, -at, -er/erna, -et, -or/orna, -en. We do not strip -an or -ans suffixes, + * since that would require a large dictionary of exceptions. * * <p>Adapted from the identically named algorithm in Apache Lucene's analysis-common module. * Instances are stateless and safe for concurrent use by multiple threads; each instance is also @@ -69,29 +68,17 @@ import static opennlp.tools.stemmer.light.StemmerUtil.endsWith; * case-folding normalization step; the stemmer does not fold case itself.</p> */ @ThreadSafe -public final class SwedishMinimalStemmer implements Stemmer, StemmerFactory { - /** {@inheritDoc} */ - @Override - public CharSequence stem(CharSequence word) { - if (word == null) { - throw new IllegalArgumentException("The word must not be null."); - } - final int length = word.length(); - final char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = word.charAt(i); - } - final int stemmedLength = stem(buffer, length); - return new String(buffer, 0, stemmedLength); - } - +public final class SwedishMinimalStemmer extends AbstractCharArrayStemmer + implements StemmerFactory { /** {@inheritDoc} */ @Override public Stemmer newStemmer() { return this; } - private int stem(char[] s, int len) { + /** {@inheritDoc} */ + @Override + int stem(char[] s, int len) { if (len > 4 && s[len - 1] == 's') len--; if (len > 6 diff --git a/opennlp-core/opennlp-runtime/src/test/java/opennlp/tools/stemmer/light/LightStemmerParityTest.java b/opennlp-core/opennlp-runtime/src/test/java/opennlp/tools/stemmer/light/LightStemmerParityTest.java index 5003b8fea..24452efd8 100644 --- a/opennlp-core/opennlp-runtime/src/test/java/opennlp/tools/stemmer/light/LightStemmerParityTest.java +++ b/opennlp-core/opennlp-runtime/src/test/java/opennlp/tools/stemmer/light/LightStemmerParityTest.java @@ -29,6 +29,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import opennlp.tools.stemmer.Stemmer; +import opennlp.tools.stemmer.StemmerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -90,9 +91,8 @@ class LightStemmerParityTest { @ParameterizedTest(name = "{0}") @MethodSource("fixtures") void testStemmerIsItsOwnFactory(String fixture, Stemmer stemmer) { - final opennlp.tools.stemmer.StemmerFactory factory = - (opennlp.tools.stemmer.StemmerFactory) stemmer; + final StemmerFactory factory = (StemmerFactory) stemmer; assertEquals(stemmer, factory.newStemmer(), - "a stateless stemmer returns itself from newStemmer()"); + "the stemmer returns itself from newStemmer()"); } }
