This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-codec.git
The following commit(s) were added to refs/heads/master by this push:
new 54ce30b4 Javadoc
54ce30b4 is described below
commit 54ce30b44ecdab4a8fcc393c0ca5f26c9c2a4f16
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Aug 6 07:50:51 2026 -0400
Javadoc
---
.../commons/codec/language/bm/PhoneticEngine.java | 157 ++++++++-------------
1 file changed, 62 insertions(+), 95 deletions(-)
diff --git
a/src/main/java/org/apache/commons/codec/language/bm/PhoneticEngine.java
b/src/main/java/org/apache/commons/codec/language/bm/PhoneticEngine.java
index 5a13a7d5..ff76db7b 100644
--- a/src/main/java/org/apache/commons/codec/language/bm/PhoneticEngine.java
+++ b/src/main/java/org/apache/commons/codec/language/bm/PhoneticEngine.java
@@ -38,15 +38,13 @@ import org.apache.commons.codec.language.bm.Rule.Phoneme;
/**
* Converts words into potential phonetic representations.
* <p>
- * This is a two-stage process. Firstly, the word is converted into a phonetic
representation that takes
- * into account the likely source language. Next, this phonetic representation
is converted into a
- * pan-European 'average' representation, allowing comparison between
different versions of essentially
- * the same word from different languages.
+ * This is a two-stage process. Firstly, the word is converted into a phonetic
representation that takes into account the likely source language. Next, this
+ * phonetic representation is converted into a pan-European 'average'
representation, allowing comparison between different versions of essentially
the same
+ * word from different languages.
* </p>
* <p>
- * This class is intentionally immutable and thread-safe.
- * If you wish to alter the settings for a PhoneticEngine, you
- * must make a new one with the updated settings.
+ * This class is intentionally immutable and thread-safe. If you wish to alter
the settings for a PhoneticEngine, you must make a new one with the updated
+ * settings.
* </p>
* <p>
* Ported from phoneticengine.php
@@ -57,20 +55,19 @@ import org.apache.commons.codec.language.bm.Rule.Phoneme;
public class PhoneticEngine {
/**
- * Utility for manipulating a set of phonemes as they are being built up.
Not intended for use outside
- * this package, and probably not outside the {@link PhoneticEngine} class.
+ * Utility for manipulating a set of phonemes as they are being built up.
Not intended for use outside this package, and probably not outside the
+ * {@link PhoneticEngine} class.
*
* @since 1.6
*/
static final class PhonemeBuilder {
/**
- * An empty builder where all phonemes must come from some set of
languages. This will contain a single
- * phoneme of zero characters. This can then be appended to. This
should be the only way to create a new
- * phoneme from scratch.
+ * An empty builder where all phonemes must come from some set of
languages. This will contain a single phoneme of zero characters. This can then
be
+ * appended to. This should be the only way to create a new phoneme
from scratch.
*
* @param languages The set of languages.
- * @return a new, empty phoneme builder.
+ * @return a new, empty phoneme builder.
*/
public static PhonemeBuilder empty(final Languages.LanguageSet
languages) {
return new PhonemeBuilder(new Rule.Phoneme("", languages));
@@ -90,7 +87,7 @@ public class PhoneticEngine {
/**
* Creates a new phoneme builder containing all phonemes in this one
extended by {@code str}.
*
- * @param str The characters to append to the phonemes.
+ * @param str The characters to append to the phonemes.
*/
public void append(final CharSequence str) {
phonemes.forEach(ph -> ph.append(str));
@@ -99,12 +96,11 @@ public class PhoneticEngine {
/**
* Applies the given phoneme expression to all phonemes in this
phoneme builder.
* <p>
- * This will lengthen phonemes that have compatible language sets to
the expression, and drop those that are
- * incompatible.
+ * This will lengthen phonemes that have compatible language sets to
the expression, and drop those that are incompatible.
* </p>
*
- * @param phonemeExpr The expression to apply.
- * @param maxPhonemes The maximum number of phonemes to build up.
+ * @param phonemeExpr The expression to apply.
+ * @param maxPhonemes The maximum number of phonemes to build up.
*/
public void apply(final Rule.PhonemeExpr phonemeExpr, final int
maxPhonemes) {
final Set<Rule.Phoneme> newPhonemes = new
LinkedHashSet<>(Math.min(phonemes.size() * phonemeExpr.size(), maxPhonemes));
@@ -129,18 +125,17 @@ public class PhoneticEngine {
/**
* Gets underlying phoneme set. Please don't mutate.
*
- * @return the phoneme set.
+ * @return the phoneme set.
*/
public Set<Rule.Phoneme> getPhonemes() {
return phonemes;
}
/**
- * Stringifies the phoneme set. This produces a single string of the
strings of each phoneme,
- * joined with a pipe. This is explicitly provided in place of
toString as it is a potentially
- * expensive operation, which should be avoided when debugging.
+ * Stringifies the phoneme set. This produces a single string of the
strings of each phoneme, joined with a pipe. This is explicitly provided in
place
+ * of toString as it is a potentially expensive operation, which
should be avoided when debugging.
*
- * @return the stringified phoneme set.
+ * @return the stringified phoneme set.
*/
public String makeString() {
return
phonemes.stream().map(Rule.Phoneme::getPhonemeText).collect(Collectors.joining("|"));
@@ -148,15 +143,13 @@ public class PhoneticEngine {
}
/**
- * A function closure capturing the application of a list of rules to an
input sequence at a particular offset.
- * After invocation, the values {@code i} and {@code found} are updated.
{@code i} points to the
- * index of the next char in {@code input} that must be processed next
(the input up to that index having been
- * processed already), and {@code found} indicates if a matching rule was
found or not. In the case where a
- * matching rule was found, {@code phonemeBuilder} is replaced with a new
builder containing the phonemes
- * updated by the matching rule.
+ * A function closure capturing the application of a list of rules to an
input sequence at a particular offset. After invocation, the values {@code i}
and
+ * {@code found} are updated. {@code i} points to the index of the next
char in {@code input} that must be processed next (the input up to that index
having
+ * been processed already), and {@code found} indicates if a matching rule
was found or not. In the case where a matching rule was found,
+ * {@code phonemeBuilder} is replaced with a new builder containing the
phonemes updated by the matching rule.
* <p>
- * Although this class is not thread-safe (it has mutable unprotected
fields), it is not shared between threads
- * as it is constructed as needed by the calling methods.
+ * Although this class is not thread-safe (it has mutable unprotected
fields), it is not shared between threads as it is constructed as needed by the
+ * calling methods.
* </p>
*
* @since 1.6
@@ -164,10 +157,15 @@ public class PhoneticEngine {
private static final class RulesApplication {
private final Map<String, List<Rule>> finalRules;
+
private final CharSequence input;
+
private final PhonemeBuilder phonemeBuilder;
+
private int i;
+
private final int maxPhonemes;
+
private boolean found;
RulesApplication(final Map<String, List<Rule>> finalRules, final
CharSequence input, final PhonemeBuilder phonemeBuilder, final int i,
@@ -189,9 +187,9 @@ public class PhoneticEngine {
}
/**
- * Invokes the rules. Loops over the rules list, stopping at the first
one that has a matching context
- * and pattern. Then applies this rule to the phoneme builder to
produce updated phonemes. If there was no
- * match, {@code i} is advanced one and the character is silently
dropped from the phonetic spelling.
+ * Invokes the rules. Loops over the rules list, stopping at the first
one that has a matching context and pattern. Then applies this rule to the
+ * phoneme builder to produce updated phonemes. If there was no match,
{@code i} is advanced one and the character is silently dropped from the
phonetic
+ * spelling.
*
* @return {@code this}.
*/
@@ -210,11 +208,9 @@ public class PhoneticEngine {
}
}
}
-
if (!found) {
patternLength = 1;
}
-
i += patternLength;
return this;
}
@@ -229,26 +225,19 @@ public class PhoneticEngine {
private static final Map<NameType, Set<String>> NAME_PREFIXES = new
EnumMap<>(NameType.class);
private static final Pattern QUOTE = Pattern.compile("'");
-
static {
- NAME_PREFIXES.put(NameType.ASHKENAZI,
- Collections.unmodifiableSet(
- new HashSet<>(Arrays.asList("bar", "ben", "da", "de",
"van", "von"))));
- NAME_PREFIXES.put(NameType.SEPHARDIC,
- Collections.unmodifiableSet(
- new HashSet<>(Arrays.asList("al", "el", "da", "dal",
"de", "del", "dela", "de la",
- "della", "des",
"di", "do", "dos", "du", "van", "von"))));
- NAME_PREFIXES.put(NameType.GENERIC,
- Collections.unmodifiableSet(
- new HashSet<>(Arrays.asList("da", "dal", "de", "del",
"dela", "de la", "della",
- "des", "di", "do",
"dos", "du", "van", "von"))));
+ NAME_PREFIXES.put(NameType.ASHKENAZI, Collections.unmodifiableSet(new
HashSet<>(Arrays.asList("bar", "ben", "da", "de", "van", "von"))));
+ NAME_PREFIXES.put(NameType.SEPHARDIC, Collections.unmodifiableSet(
+ new HashSet<>(Arrays.asList("al", "el", "da", "dal", "de",
"del", "dela", "de la", "della", "des", "di", "do", "dos", "du", "van",
"von"))));
+ NAME_PREFIXES.put(NameType.GENERIC, Collections.unmodifiableSet(
+ new HashSet<>(Arrays.asList("da", "dal", "de", "del", "dela",
"de la", "della", "des", "di", "do", "dos", "du", "van", "von"))));
}
/**
* Joins some strings with an internal separator.
*
- * @param strings Strings to join.
- * @param sep String to separate them with.
+ * @param strings Strings to join.
+ * @param sep String to separate them with.
* @return A single String consisting of each element of {@code strings}
interleaved by {@code sep}.
*/
private static String join(final List<String> strings, final String sep) {
@@ -268,12 +257,9 @@ public class PhoneticEngine {
/**
* Generates a new, fully-configured phonetic engine.
*
- * @param nameType
- * the type of names it will use.
- * @param ruleType
- * the type of rules it will apply.
- * @param concatenate
- * if it will concatenate multiple encodings.
+ * @param nameType the type of names it will use.
+ * @param ruleType the type of rules it will apply.
+ * @param concatenate if it will concatenate multiple encodings.
*/
public PhoneticEngine(final NameType nameType, final RuleType ruleType,
final boolean concatenate) {
this(nameType, ruleType, concatenate, DEFAULT_MAX_PHONEMES);
@@ -282,14 +268,10 @@ public class PhoneticEngine {
/**
* Generates a new, fully-configured phonetic engine.
*
- * @param nameType
- * the type of names it will use.
- * @param ruleType
- * the type of rules it will apply.
- * @param concatenate
- * if it will concatenate multiple encodings.
- * @param maxPhonemes
- * the maximum number of phonemes that will be handled.
+ * @param nameType the type of names it will use.
+ * @param ruleType the type of rules it will apply.
+ * @param concatenate if it will concatenate multiple encodings.
+ * @param maxPhonemes the maximum number of phonemes that will be handled.
* @since 1.7
*/
public PhoneticEngine(final NameType nameType, final RuleType ruleType,
final boolean concatenate, final int maxPhonemes) {
@@ -304,39 +286,31 @@ public class PhoneticEngine {
}
/**
- * Applies the final rules to convert from a language-specific phonetic
representation to a
- * language-independent representation.
+ * Applies the final rules to convert from a language-specific phonetic
representation to a language-independent representation.
*
* @param phonemeBuilder The current phonemes.
- * @param finalRules The final rules to apply.
+ * @param finalRules The final rules to apply.
* @return The resulting phonemes.
*/
- private PhonemeBuilder applyFinalRules(final PhonemeBuilder phonemeBuilder,
- final Map<String, List<Rule>> finalRules) {
+ private PhonemeBuilder applyFinalRules(final PhonemeBuilder
phonemeBuilder, final Map<String, List<Rule>> finalRules) {
Objects.requireNonNull(finalRules, "finalRules");
if (finalRules.isEmpty()) {
return phonemeBuilder;
}
-
final Map<Rule.Phoneme, Rule.Phoneme> phonemes = new
TreeMap<>(Rule.Phoneme.COMPARATOR);
-
phonemeBuilder.getPhonemes().forEach(phoneme -> {
PhonemeBuilder subBuilder =
PhonemeBuilder.empty(phoneme.getLanguages());
final CharSequence phonemeText = phoneme.getPhonemeText();
-
for (int i = 0; i < phonemeText.length();) {
final RulesApplication rulesApplication = new
RulesApplication(finalRules, phonemeText, subBuilder, i, maxPhonemes).invoke();
final boolean found = rulesApplication.isFound();
subBuilder = rulesApplication.getPhonemeBuilder();
-
if (!found) {
// not found, appending as-is
subBuilder.append(phonemeText.subSequence(i, i + 1));
}
-
i = rulesApplication.getI();
}
-
// the phonemes map orders the phonemes only based on their text,
but ignores the language set
// when adding new phonemes, check for equal phonemes and merge
their language set, otherwise
// phonemes with the same text but different language set get lost
@@ -350,15 +324,13 @@ public class PhoneticEngine {
}
});
});
-
return new PhonemeBuilder(phonemes.keySet());
}
/**
* Encodes a string to its phonetic representation.
*
- * @param input
- * the String to encode.
+ * @param input the String to encode.
* @return The encoding of the input.
*/
public String encode(final String input) {
@@ -368,12 +340,9 @@ public class PhoneticEngine {
/**
* Encodes an input string into an output phonetic representation, given a
set of possible origin languages.
*
- * @param input
- * String to phoneticise; a String with dashes or spaces
separating each word.
- * @param languageSet
- * set of possible origin languages.
- * @return A phonetic representation of the input; a String containing
'-'-separated phonetic representations of the
- * input.
+ * @param input String to phoneticise; a String with dashes or
spaces separating each word.
+ * @param languageSet set of possible origin languages.
+ * @return A phonetic representation of the input; a String containing
'-'-separated phonetic representations of the input.
*/
public String encode(String input, final Languages.LanguageSet
languageSet) {
final Map<String, List<Rule>> rules =
Rule.getInstanceMap(this.nameType, RuleType.RULES, languageSet);
@@ -381,11 +350,9 @@ public class PhoneticEngine {
final Map<String, List<Rule>> finalRules1 =
Rule.getInstanceMap(this.nameType, this.ruleType, "common");
// rules that apply to a specific language that may be ambiguous or
wrong if applied to other languages
final Map<String, List<Rule>> finalRules2 =
Rule.getInstanceMap(this.nameType, this.ruleType, languageSet);
-
// tidy the input
// lower case is a locale-dependent operation
input = input.toLowerCase(Locale.ENGLISH).replace('-', ' ').trim();
-
if (this.nameType == NameType.GENERIC) {
final String dQuotePrefix = "d'";
final int dqpLen = dQuotePrefix.length();
@@ -407,10 +374,8 @@ public class PhoneticEngine {
}
}
}
-
final List<String> words =
Arrays.asList(ResourceConstants.SPACES.split(input));
final List<String> words2 = new ArrayList<>();
-
// special-case handling of word prefixes based upon the name type
switch (this.nameType) {
case SEPHARDIC:
@@ -430,7 +395,6 @@ public class PhoneticEngine {
default:
throw new IllegalStateException("Unreachable case: " +
this.nameType);
}
-
if (this.concat) {
// concat mode enabled
input = join(words2, " ");
@@ -444,22 +408,17 @@ public class PhoneticEngine {
// return the result without the leading "-"
return result.substring(1);
}
-
PhonemeBuilder phonemeBuilder = PhonemeBuilder.empty(languageSet);
-
// loop over each char in the input - we will handle the increment
manually
for (int i = 0; i < input.length();) {
- final RulesApplication rulesApplication =
- new RulesApplication(rules, input, phonemeBuilder, i,
maxPhonemes).invoke();
+ final RulesApplication rulesApplication = new
RulesApplication(rules, input, phonemeBuilder, i, maxPhonemes).invoke();
i = rulesApplication.getI();
phonemeBuilder = rulesApplication.getPhonemeBuilder();
}
-
// Apply the general rules
phonemeBuilder = applyFinalRules(phonemeBuilder, finalRules1);
// Apply the language-specific rules
phonemeBuilder = applyFinalRules(phonemeBuilder, finalRules2);
-
return phonemeBuilder.makeString();
}
@@ -509,6 +468,14 @@ public class PhoneticEngine {
return this.concat;
}
+ /**
+ * Finds the last occurrence of a repeating prefix in a string, starting
from the beginning of the string and moving forward.
+ *
+ * @param source The source string to search within.
+ * @param prefix The prefix to look for.
+ * @param prefixLen The length of the prefix.
+ * @return The index in the source string where the last occurrence of the
repeating prefix ends.
+ */
private int lastRepeat(final String source, final String prefix, final int
prefixLen) {
// Find without allocating new string.
int start = 0;