Repository navigation
OPENNLP-1929: Regex Removal (7/10): BasicContextGenerator takes the separator literally and splits on Unicode whitespace by default - #1280
Conversation
2be805f to
021a3a2
Compare
021a3a2 to
2842f53
Compare
rzo1
left a comment
There was a problem hiding this comment.
Little time, so here is a GPT 5.6-sol review instead for now
Compatibility note. No additional splitting bug found; please document the intentional constructor behavior changes.
Validation across the combined stack: 1,856 targeted tests, zero failures, one skipped.
| * Must not be {@code null} or empty. | ||
| * @throws IllegalArgumentException If {@code sep} is {@code null} or empty. | ||
| */ | ||
| public BasicContextGenerator(String sep) { |
There was a problem hiding this comment.
This intentionally changes existing constructor behavior: regex separators such as \s+ become literal, and empty separators now throw. Please include these changes in the migration notes.
2842f53 to
112638e
Compare
|
Here are some additional comments. The literal-separator change breaks existing callers silently, so it needs a deprecation path or a documented maintainer decision. Blocking
Minor
Verified: the literal path is correct (1M fuzz inputs vs |
|
Follow-up: the shared |
0a397d2 to
3db4708
Compare
ab2b246 to
8f54db1
Compare
|
I did a break instead of a deprecated regex constructor. We shouldn't keep a I think it's cleaner to have a separator with a backslash to throw at construction, so old escapes fails loudly. |
|
Thanks, the earlier points are addressed. I'm fine with the hard break instead of keeping a
Apply it with 1280.patchFrom 9f1049aabcb8819d1633c1b21959efacf7e61a70 Mon Sep 17 00:00:00 2001
From: Richard Zowalla <rzo1@apache.org>
Date: Fri, 25 Sep 2026 21:09:29 +0200
Subject: [PATCH] OPENNLP-1929: Document the breaking change and trim
commentary
- README: list the literal separator and the backslash rejection under
breaking changes in the 2.x to 3.x migration section.
- BasicContextGenerator: reduce the sep parameter doc to its constraints.
- BasicContextGeneratorTest: drop the 2.x history comment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---
README.md | 6 +++++-
.../java/opennlp/tools/ml/maxent/BasicContextGenerator.java | 6 ++----
.../opennlp/tools/ml/maxent/BasicContextGeneratorTest.java | 2 --
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index 5a40f38e4..e02bd2598 100644
--- a/README.md
+++ b/README.md
@@ -120,7 +120,7 @@ For more details please check our [documentation](https://opennlp.apache.org/doc
## Migrating from 2.x to 3.x
-The 3.x release line of Apache OpenNLP introduces **no** known breaking changes but modularizes the project for better usage as a library and to support future extensibility.
+The 3.x release line of Apache OpenNLP introduces only a few breaking changes, listed below, but modularizes the project for better usage as a library and to support future extensibility.
The core API remains stable and compatible with 2.x, but the project structure has been reorganized into multiple modules.
That means, that you can continue to use the previous `opennlp-tools` artifact as a dependency. However, we strongly recommend to switch to the new modular structure
@@ -129,6 +129,10 @@ and import only the components you need, which will result in a smaller dependen
Only `opennlp-runtime` needs to be added as a dependency, and you can add additional modules (e.g. `opennlp-ml-maxent`, `opennlp-model-resolver`, etc.) as required by your project.
For users of the traditional CLI toolkit, nothing changes with the 3.x release line. CLI usage remains stable as described in the [project's dev manual](https://opennlp.apache.org/docs/).
+### Breaking changes
+
+- `BasicContextGenerator(String)`: the separator is taken literally instead of as a regular expression, and a separator containing a backslash throws `IllegalArgumentException`. Empty predicates are no longer returned, and the default constructor splits on runs of Unicode whitespace ([OPENNLP-1929](https://issues.apache.org/jira/browse/OPENNLP-1929)).
+
### Thread safety
Starting with 3.0.0, the core `*ME` classes (`POSTaggerME`, `TokenizerME`, `SentenceDetectorME`, `ChunkerME`, `LemmatizerME`, `NameFinderME`) are thread safe and a single instance can be shared across threads. This eliminates the need to pool or recreate ME instances per thread. The legacy `ThreadSafe*ME` wrappers from 2.x still work but are now deprecated; existing code does not need to change to upgrade.
diff --git a/opennlp-core/opennlp-ml/opennlp-ml-maxent/src/main/java/opennlp/tools/ml/maxent/BasicContextGenerator.java b/opennlp-core/opennlp-ml/opennlp-ml-maxent/src/main/java/opennlp/tools/ml/maxent/BasicContextGenerator.java
index fdebad954..118c12090 100644
--- a/opennlp-core/opennlp-ml/opennlp-ml-maxent/src/main/java/opennlp/tools/ml/maxent/BasicContextGenerator.java
+++ b/opennlp-core/opennlp-ml/opennlp-ml-maxent/src/main/java/opennlp/tools/ml/maxent/BasicContextGenerator.java
@@ -59,10 +59,8 @@ public class BasicContextGenerator implements ContextGenerator<String> {
* whitespace is part of the predicates.
*
* @param sep The separator, taken as written and not as a regular expression.
- * Must not be {@code null} or empty, and must be well-formed text: an unpaired
- * surrogate is not a character and could split a code point of the input.
- * A backslash is rejected, so a separator escaped for the regular expression
- * engine, such as {@code "\\|"}, fails here instead of splitting nothing.
+ * Must not be {@code null} or empty, and must not contain a backslash or
+ * an unpaired surrogate.
* @throws IllegalArgumentException If {@code sep} is {@code null}, empty, contains a
* backslash, or contains an unpaired surrogate.
*/
diff --git a/opennlp-core/opennlp-ml/opennlp-ml-maxent/src/test/java/opennlp/tools/ml/maxent/BasicContextGeneratorTest.java b/opennlp-core/opennlp-ml/opennlp-ml-maxent/src/test/java/opennlp/tools/ml/maxent/BasicContextGeneratorTest.java
index c68b9b0d2..db69eda8d 100644
--- a/opennlp-core/opennlp-ml/opennlp-ml-maxent/src/test/java/opennlp/tools/ml/maxent/BasicContextGeneratorTest.java
+++ b/opennlp-core/opennlp-ml/opennlp-ml-maxent/src/test/java/opennlp/tools/ml/maxent/BasicContextGeneratorTest.java
@@ -121,8 +121,6 @@ public class BasicContextGeneratorTest {
@ParameterizedTest
@ValueSource(strings = {"\\|", "\\.", "\\s", "\\s+", "\\Q|\\E", "a\\b", "\\"})
void testEscapedSeparatorIsRejected(String separator) {
- // the escape that a regex separator needed fails at construction, so a separator written
- // for 2.x cannot silently stop splitting
IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class,
() -> new BasicContextGenerator(separator));
Assertions.assertEquals("sep is taken as written and must not contain a backslash: "
--
2.55.0
Still open on your side:
As mentioned on #1278: pushing your branches to |
43da7b5 to
19b54ff
Compare
|
Thanks. I squashed everything with your patch applied. The migration notes are in the manual paragraph in machine-learning.xml, including the table of old regex separators and what to pass now, and the description names the break. The title uses the wording you suggested. For now on, I'll create PRs from an apache branch. I'll try to minimize the churn after an PR is created. |
There was a problem hiding this comment.
Thanks for the update. A few things don't match what we discussed:
-
The separator check got wider than agreed. I was fine with rejecting a backslash, but the head now also rejects
[ ] ( ) { } | * + ? ^ $ .That means"|"and"."can't be used as separators at all, which is odd given the ticket is about"|"not working. Both never worked as regex separators, so nobody can depend on the old behaviour. Please limit the check to the backslash, or explain why we need the wider one. -
The description and the commit message don't match the code anymore.
"[,;]"is described as a plain separator, but the test expects it to throw. There is also no migration table inmachine-learning.xml, and only the backslash is mentioned as breaking. -
The README part of my patch is missing. It still says there are no known breaking changes.
-
The title is unchanged. Suggestion: "OPENNLP-1929: Regex Removal (7/10): BasicContextGenerator takes the separator literally and splits on Unicode whitespace by default".
-
Please rebase, the branch is 14 commits behind main.
Minor:
BasicContextGeneratorTest:106-107: the history comment is back in other words. Please drop it.machine-learning.xml:144: unrelated newline change at the end of the file. Please revert.- The list of rejected characters is in the Javadoc and in the manual. One place is enough.
The rest of my earlier comments is addressed. No eval build needed, since nothing in the repo calls these constructors.
19b54ff to
5f65a6b
Compare
|
Yeh, the head I pushed had the wider check and neither the README hunk nor the table. Now only a backslash is rejected, so Your README hunk is applied, the manual has the table of 2.x separators and what to pass now, the test comment and the end-of-file newline change are gone, and the branch is rebased onto main. Retitled as you had suggested. |
|
@krickert Please rebase. |
This changes BasicContextGenerator to treat custom separators literally.
The default constructor splits on runs of Unicode whitespace, so repeated
spaces no longer produce empty predicates.
AI assisted summary for accuracy, edited for clarity:
> Previously, getContext used String.split(separator), compiling the
> separator as a regex on every call. Separators such as "|" and "." gave
> unexpected results, while "+" and "(" threw PatternSyntaxException
> during prediction.
> The default uses StringUtil.splitOnUnicodeWhitespace regardless of
> opennlp.whitespace.mode. Custom separators are matched literally, and
> empty predicates are omitted. Null input and null, empty or malformed
> separators (unpaired surrogates) throw IllegalArgumentException.
> Backslashes in separators are rejected at construction, so old regex
> escapes such as "\\|" and "\\s+" fail at startup. No code in the
> repository uses the separator constructor.
> The manual explains the behavior and includes a migration table.
> BasicContextGeneratorTest has 129 cases that fail on the old code.
Assisted-by: Claude Code 2.1.281
Co-authored-by: Kristian Rickert <kristian@apache.org>
Co-authored-by: Claude <noreply@anthropic.com>
Reviewed-by: Kristian Rickert <kristian@apache.org>
Signed-off-by: Kristian Rickert <kristian@apache.org>
…k in the README and add the separator table to the manual
5f65a6b to
c9bf658
Compare
|
Rebased onto main. The README now lists this break under the "Breaking changes" section that #1278 added, so both bullets are there. |
… helpers ConlluStream splits a range id on the literal dash. TokenSampleStream and DownloadUtil split on whitespace runs under the active whitespace mode. The forbidden list message names the helpers that exist now. BasicContextGenerator's call is replaced by #1280.
BasicContextGenerator.getContextsplit onString.split(separator), so the separator was compiled as a regex on each call:"|"split on each character,"."returned an empty array, and"+"or"("failed withPatternSyntaxExceptionat prediction time.Changes:
StringUtil.splitOnUnicodeWhitespace), independent ofopennlp.whitespace.mode."a b"now gives[a, b], not[a, , b].BasicContextGenerator(String sep)takes the separator as written.IllegalArgumentException.machine-learning.xmlstates these rules and has the migration table.Breaking for code that passed a regex: a separator containing a backslash, such as
"\\|"or"\\s+", now throwsIllegalArgumentExceptionat construction, so the old escapes fail at startup instead of matching no input."[,;]"is a plain separator. No code in the repository uses the separator constructor.OPENNLP-1929