OPENNLP-1880: Add a Lexical knowledge base seam with WN-LMF and WNDB readers and a Morphy lemmatizer - #1155
Draft
krickert wants to merge 19 commits into
Draft
OPENNLP-1880: Add a Lexical knowledge base seam with WN-LMF and WNDB readers and a Morphy lemmatizer#1155krickert wants to merge 19 commits into
krickert wants to merge 19 commits into
Conversation
mawiesne
marked this pull request as draft
July 8, 2026 04:33
krickert
force-pushed
the
wordnet-api
branch
2 times, most recently
from
July 8, 2026 14:44
1a70da6 to
d01ddff
Compare
krickert
marked this pull request as ready for review
July 8, 2026 15:07
krickert
marked this pull request as draft
July 8, 2026 16:18
Contributor
Author
|
There's a minor windows-specific build issue due to line endings and Windoze. Will flip back to review once I find my windows machine and test. Code is the same though. |
krickert
force-pushed
the
wordnet-api
branch
3 times, most recently
from
July 9, 2026 19:19
0551c1d to
d01ddff
Compare
Contributor
|
Hi @krickert - as mentioned on Slack I currently dont have the time for a manual review but I just let Fable do a comprehensive review on this PR. Here is the result:
Human review will follow. |
Contributor
Author
|
Thanks, every finding is addressed in 69ead68. Point by point:
Module tests go from 86 to 112; reactor verify is clean. |
krickert
force-pushed
the
wordnet-api
branch
3 times, most recently
from
July 19, 2026 11:41
6534cdb to
8878e03
Compare
krickert
added a commit
to ai-pipestream/opennlp
that referenced
this pull request
Jul 24, 2026
…ENNLP-1895 recorded Restate the map against apache main a864230, cut as 3.0.0-M5 on 2026-07-24. apache#1177 (OPENNLP-1870) merged upstream and moves into the merged box, apache#1190 and apache#1191 are marked ready for review, and OPENNLP-1895 (quantized embedding tables) joins the diagram in its own colour: filed in JIRA with the pull request deliberately held until apache#1165 and apache#1152 move. Statuses now carry the measured GitHub draft flag and how far each head sits behind main, which surfaces three things the old text did not: apache#1182 is a draft again, apache#1167 is based on main rather than on apache#1155 and carries the seam and isBlank commits as copies, and apache#1152 reports conflicts only because its apache-hosted sentencepiece base has diverged from the refreshed head.
krickert
force-pushed
the
wordnet-api
branch
2 times, most recently
from
July 31, 2026 10:38
09ab84d to
d5f99e3
Compare
krickert
added a commit
that referenced
this pull request
Aug 15, 2026
# Conflicts: # opennlp-distr/pom.xml # opennlp-distr/src/main/assembly/bin.xml # opennlp-docs/src/docbkx/opennlp.xml # pom.xml # rat-excludes
krickert
added a commit
that referenced
this pull request
Aug 16, 2026
krickert
added a commit
that referenced
this pull request
Aug 30, 2026
# Conflicts: # opennlp-distr/pom.xml # opennlp-distr/src/main/assembly/bin.xml # pom.xml # rat-excludes
…s and a Morphy lemmatizer Adds the LexicalKnowledgeBase contract in opennlp.tools.wordnet and the opennlp-wordnet module implementing it twice: WnLmfReader for WN-LMF XML and WndbReader for the legacy WNDB database files, with reader-equivalence coverage over miniature fixtures of both formats. The WN-LMF reader skips DOCTYPE declarations unresolved with DTD support off, so Open English WordNet releases parse unmodified while entity expansion stays closed; the WNDB fixtures are pinned to LF so their embedded byte offsets survive Windows checkout. The Morphy lemmatizer resolves inflected forms through suffix rules and the format's exception lists. Null arguments fail loudly with IllegalArgumentException, malformed data raises the checked InvalidFormatException, and the public seam carries no brand name: WordNet stays in the names of the classes that actually read WordNet formats.
Add docbkx/wordnet.xml, wire it into the manual, and add WordNetUsageExampleTest asserting the lookups the chapter prints.
…ment with the review conventions
…undary, pin WNDB rejections - Narrow the LexicalKnowledgeBase javadoc so the interface stops prescribing what only an implementation can promise: lemma matching semantics and thread safety are now stated as implementation specific and documented where they hold, on InMemoryWordNetLexicon, which already carries @threadsafe and describes the folding it applies. - Reword the contains() javadoc to say plainly that the default implementation delegates to lookup(), instead of speculating about cheaper overrides. - Move the null-element checks in MorphyLemmatizer up to the public lemmatize() overloads, both the array form and the list form, so validation happens once at the boundary the caller sees; the private lemmasOf() no longer repeats them and now documents that its arguments are validated by the caller. - Reject a null argument in LemmaFolding.splitOnSpaces() rather than letting it fail later as a NullPointerException, and capitalize the fold() message so it matches the wording the other validators use. - Document the throws clauses that the explicit validation adds, on LemmaKey.of() and on splitOnSpaces(). - Extract the repeated WN-LMF attribute names into ID_ATTRIBUTE, PART_OF_SPEECH_ATTRIBUTE, REL_TYPE_ATTRIBUTE, and TARGET_ATTRIBUTE, and the shared error opening into MALFORMED_PREFIX, so the element handlers stop repeating string literals. - Extract the WNDB offset failure detail into MALFORMED_OFFSET, shared by the length check and the digit check. - Fold the duplicated WNDB message construction into malformedMessage(), so the tokenizer builds the text directly instead of constructing an InvalidFormatException only to read getMessage() back off it. - Drop the redundant fileName parameter from WndbReader.readAll(), which already names the full path it failed to open. - Reduce the visibility of the Parser helpers in WnLmfReader: malformed() and line() are now private instance methods like every other helper in that class. - Make MorphyLemmatizer.rulesFor() an instance method for the same reason, so the lemmatizer's private helpers are consistent. - Add the missing javadoc on the RELATION_NAMES and POINTER_SYMBOLS lookup tables and on both RawSynset holders, the last undocumented members in the readers. - Correct two stale comments: the build() comment now points at memberLemmas(), where the synset and member part-of-speech agreement is really checked, and the mutate() comment in the tests states the actual constraint, that an edit which changes a line's length is only safe when the reader is expected to fail on that line before it reads the ones after it. - Add a parameterized WNDB test pinning eight field-level rejections that had no coverage: the offset length and digit checks, the synset and index part of speech mismatches, the base-16 word count field, the minimum word count, the pointer pos, the gloss separator, and the syntactic marker. - Add pinning tests for the newly explicit validation: splitOnSpaces() on null, and the list lemmatize() overload with a null token and with a null tag. - Share the fixtures instead of duplicating them: WndbReaderTest now exposes DOG_ID, CANID_ID, and its fixtureDirectory(), WnLmfReaderTest exposes fixture(), and LexiconConcurrencyTest and WordNetUsageExampleTest use those instead of their own loader copies and hardcoded ids. - Document the package-private test fixture helpers and switch WordNetUsageExampleTest to static assertion imports, matching the other tests in the module.
…reader-test gaps Add a Navigating relations section to the WordNet chapter and mirror it, pin the chapter's Path-based loading listing with a usage-test method, give the exception fixture a two-base-form entry (axes to axis, ax) so the list-form lemmatizer path is exercised with multiple candidates, and fold the copy-paste WN-LMF rejection tests into one parameterized ladder with identical documents and message pins.
…-lexicon resources Extends WordNetRelation with every typed synset and sense relation in the WN-LMF 1.4 DTD and rejects relations placed on the wrong element kind. Adds WnLmfReader.readResource with WnLmfResource, WnLmfLexicon, and WnLmfDependency so multi-lexicon documents keep per-language lookup indexes and Requires metadata; the single-lexicon read methods reject that shape instead of merging, and LexiconExtension is rejected clearly. Unlexicalized synsets used by real OMW releases are retained as relation targets with empty lemma lists. Includes relation-conformance and resource tests, an optional pinned OMW 2.0 integration check behind dev/test-omw-wordnets.sh, and manual coverage with mirror tests.
The canonical lemma fold used String.toLowerCase(Locale.ROOT), which applies the SpecialCasing one-to-many mappings. Fold with StringUtil.toLowerCase instead so the sense-index keys, the Morphy exception keys, and the query path all share the locale-independent one-to-one mapping.
…plainly Repeated exception message literals become constants, and the seam, reader, and lemmatizer Javadoc and the manual chapter say what the types do in plainer terms.
krickert
force-pushed
the
wordnet-api
branch
from
September 4, 2026 23:04
9c0e9a0 to
19791cf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebased on main; the diff is this change only (53 files, all new except one line in the extensions pom).
Adds the format-agnostic lexical knowledge base interface to opennlp-api (
opennlp.tools.wordnet:LexicalKnowledgeBase,Synset,WordNetPOS,WordNetRelation), and a newopennlp-extensions/opennlp-wordnetmodule with two clean-room readers behind it:WnLmfReader: a StAX reader for WN-LMF XML, Open English WordNet's interchange format. XXE-hardened per the OWASP posture for DOCTYPE-bearing formats: the DOCTYPE line real OEWN releases ship is tokenized and skipped while external entities and the external DTD subset stay fully disabled, so an unmodified download parses directly; a dedicated test proves a DOCTYPE-declared internal-subset entity payload fails loud rather than expanding.WndbReader: a reader for legacy Princeton WNDB directories, validating the format's documented offset contract.Both produce equivalent immutable, thread-safe lexicon views, pinned by a structural equivalence test over matching miniature fixtures. On top of the interface,
MorphyLemmatizerimplements the existingLemmatizerinterface with the documented Morphy algorithm: exception-list lookup first, then per-POS detachment rules with each candidate validated against the lexicon.Naming: the interface is
LexicalKnowledgeBaserather than a WordNet-branded name; the contract is generic and only the format-specific readers use the WordNet name.The module ships code only; users point it at a WordNet database they downloaded. Verified against Princeton 3.0 (117,659 synsets) and OEWN 2024 (120,630 synsets). No third-party WordNet library is used or referenced.
Review rounds
The 2026-07-10 findings are in (see the reply below them). The 2026-09-04 conventions from the sibling PRs are in as well: Javadoc on every method and constructor with
@throwson the validating paths, validation at the public boundary, repeated message literals as constants,StringUtil.toLowerCasefor lemma folding, bounded WNDB index counts and markers, relation and index integrity checks, caller-owned streams preserved, and failing-test commits ahead of each fix.Verification
opennlp-apiandopennlp-wordnetbuild with checkstyle and forbidden APIs; the module's 347 tests pass (4 skipped without a downloaded wordnet) plus the 18 interface tests in api. The manual chapter's examples are mirrored byWordNetUsageExampleTest.Small upstream note found along the way:
DictionaryLemmatizerjavadoc says unknown = "0" but the code returns the letter "O"; Morphy matches the code. Filed separately.https://issues.apache.org/jira/browse/OPENNLP-1880