Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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.wordnet;

import java.util.List;
import java.util.Optional;

/**
* Lemma and synset lookup over a loaded lexical-semantic resource in the WordNet family. Synset
* identity is opaque and source-qualified (see {@link Synset#id()}). Lookups return their matches
* in the source's sense order and never return {@code null}.
*
* <p>Lemma matching semantics are the implementation's concern. The reference implementations
* match case-insensitively (case folding with the root locale) and treat the underscore some
* formats store in multiword lemmas as a space; an implementation with different semantics must
* document them. Returned {@link Synset#lemmas() lemmas} preserve the source's written forms,
* with spaces in multiword lemmas.</p>
*
* <p>Implementations must be immutable and thread-safe after loading: one instance is meant to
* be shared across an application's threads for concurrent lookups.</p>
*/
public interface LexicalKnowledgeBase {

/**
* Finds the synsets containing a lemma with a part of speech, in the source's sense order
* (the most salient sense first when the source ranks senses).
*
* @param lemma The lemma to look up. Must not be {@code null}.
* @param pos The part of speech to look it up as. Must not be {@code null}.
* @return The matching synsets, never {@code null}; empty when the lexicon does not contain
* the lemma with that part of speech.
* @throws IllegalArgumentException Thrown if {@code lemma} or {@code pos} is {@code null}.
*/
List<Synset> lookup(String lemma, WordNetPOS pos);

/**
* Finds a synset by its opaque identifier.
*
* @param synsetId The synset identifier, as minted by this lexicon. Must not be {@code null}.
* @return The synset, or empty when this lexicon has no synset with that identifier.
* @throws IllegalArgumentException Thrown if {@code synsetId} is {@code null}.
*/
Optional<Synset> synset(String synsetId);

/**
* Navigates one typed relation from a synset.
*
* @param synsetId The source synset identifier. Must not be {@code null}.
* @param relation The relation type to follow. Must not be {@code null}.
* @return The target synset ids in source order, never {@code null}; empty when the synset is
* unknown or has no relation of that type.
* @throws IllegalArgumentException Thrown if {@code synsetId} or {@code relation} is
* {@code null}.
*/
default List<String> related(String synsetId, WordNetRelation relation) {
if (relation == null) {
throw new IllegalArgumentException("Relation must not be null");
}
return synset(synsetId).map(s -> s.related(relation)).orElse(List.of());
}

/**
* Tests whether the lexicon contains a lemma with a part of speech. This is the membership
* check morphological rules validate their candidates against; implementations may override
* it with a cheaper check than {@link #lookup(String, WordNetPOS)}.
*
* @param lemma The lemma to test. Must not be {@code null}.
* @param pos The part of speech to test it as. Must not be {@code null}.
* @return {@code true} if the lexicon contains the lemma with that part of speech.
* @throws IllegalArgumentException Thrown if {@code lemma} or {@code pos} is {@code null}.
*/
default boolean contains(String lemma, WordNetPOS pos) {
return !lookup(lemma, pos).isEmpty();
}
}
123 changes: 123 additions & 0 deletions opennlp-api/src/main/java/opennlp/tools/wordnet/Synset.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* 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.wordnet;

import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

import opennlp.tools.commons.ThreadSafe;

/**
* One synonym set: a single lexicalized concept with its member lemmas, gloss, and typed
* relations to other synsets.
*
* <p>The {@link #id() id} is an opaque, source-qualified string minted by the reader that
* produced the synset; consumers must not parse it, only pass it back to
* {@link LexicalKnowledgeBase#synset(String)} and compare it for equality. Relations map each
* {@link WordNetRelation} present on this synset to the target synset ids in source order.</p>
*
* <p>Instances are immutable and thread-safe: the list and map components are defensively
* copied to immutable views at construction.</p>
*
* @param id The opaque, source-qualified synset identifier. Must not be {@code null} or
* empty.
* @param pos The part of speech. Must not be {@code null}.
* @param lemmas The member lemmas in source order, human-readable (multiword lemmas use
* spaces, not the underscores some formats store). Must not be {@code null} or
* empty, and must not contain {@code null} or empty elements.
* @param gloss The definition text, possibly empty when the source has none. Must not be
* {@code null}.
* @param relations The typed relations, each mapping to the target synset ids in source order.
* Must not be {@code null}; keys must not be {@code null}; each value must be
* a non-empty list of non-{@code null}, non-empty target ids.
*/
@ThreadSafe
public record Synset(
String id,
WordNetPOS pos,
List<String> lemmas,
String gloss,
Map<WordNetRelation, List<String>> relations) {

/**
* Creates a synset.
*
* @throws IllegalArgumentException Thrown if any component violates its documented constraint.
*/
public Synset {
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException("Id must not be null or empty");
}
if (pos == null) {
throw new IllegalArgumentException("Pos must not be null");
}
if (lemmas == null || lemmas.isEmpty()) {
throw new IllegalArgumentException("Lemmas must not be null or empty for synset " + id);
}
for (final String lemma : lemmas) {
if (lemma == null || lemma.isEmpty()) {
throw new IllegalArgumentException(
"Lemmas must not contain a null or empty element for synset " + id);
}
}
if (gloss == null) {
throw new IllegalArgumentException("Gloss must not be null for synset " + id);
}
if (relations == null) {
throw new IllegalArgumentException("Relations must not be null for synset " + id);
}
final Map<WordNetRelation, List<String>> copiedRelations =
new EnumMap<>(WordNetRelation.class);
for (final Map.Entry<WordNetRelation, List<String>> relation : relations.entrySet()) {
if (relation.getKey() == null) {
throw new IllegalArgumentException("Relations must not contain a null key for synset " + id);
}
final List<String> targets = relation.getValue();
if (targets == null || targets.isEmpty()) {
throw new IllegalArgumentException("Relation " + relation.getKey()
+ " must map to a non-empty target list for synset " + id);
}
for (final String target : targets) {
if (target == null || target.isEmpty()) {
throw new IllegalArgumentException("Relation " + relation.getKey()
+ " must not contain a null or empty target id for synset " + id);
}
}
copiedRelations.put(relation.getKey(), List.copyOf(targets));
}
lemmas = List.copyOf(lemmas);
relations = Collections.unmodifiableMap(copiedRelations);
}

/**
* Finds the target synset ids of one relation type.
*
* @param relation The relation type. Must not be {@code null}.
* @return The target synset ids in source order, never {@code null}; empty when this synset
* has no relation of that type.
* @throws IllegalArgumentException Thrown if {@code relation} is {@code null}.
*/
public List<String> related(WordNetRelation relation) {
if (relation == null) {
throw new IllegalArgumentException("Relation must not be null");
}
final List<String> targets = relations.get(relation);
return targets == null ? List.of() : targets;
}
}
40 changes: 40 additions & 0 deletions opennlp-api/src/main/java/opennlp/tools/wordnet/WordNetPOS.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.wordnet;

/**
* The four parts of speech a wordnet-style lexicon distinguishes.
*
* <p>The enum carries none of the single-letter codes the on-disk formats use; readers own the
* mapping from their format's codes to these values. Adjective satellites normalize to
* {@link #ADJECTIVE}, with the cluster structure preserved through
* {@link WordNetRelation#SIMILAR_TO}.</p>
*/
public enum WordNetPOS {

/** Nouns. */
NOUN,

/** Verbs. */
VERB,

/** Adjectives, including adjective satellites. */
ADJECTIVE,

/** Adverbs. */
ADVERB
}
115 changes: 115 additions & 0 deletions opennlp-api/src/main/java/opennlp/tools/wordnet/WordNetRelation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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.wordnet;

/**
* The typed relations a wordnet-style lexicon draws between {@link Synset synsets}. Readers map
* their source format's relation names onto these values.
*
* <p>Relations that a source format draws between individual word senses (antonymy and
* derivation, for example) surface here at the synset level: the synset containing the source
* sense carries the relation to the synset containing the target sense.</p>
*/
public enum WordNetRelation {

/** Opposition in meaning, for example between the adjectives for tall and short. */
ANTONYM,

/** The more general concept: a dog is a kind of canid. */
HYPERNYM,

/** The class a named instance belongs to: a specific river is an instance of river. */
INSTANCE_HYPERNYM,

/** The more specific concept: canid has the hyponym dog. */
HYPONYM,

/** A named instance of this class. */
INSTANCE_HYPONYM,

/** The group this synset is a member of. */
MEMBER_HOLONYM,

/** The whole this synset is a substance of. */
SUBSTANCE_HOLONYM,

/** The whole this synset is a part of. */
PART_HOLONYM,

/** A member of this group. */
MEMBER_MERONYM,

/** A substance this synset is made of. */
SUBSTANCE_MERONYM,

/** A part of this synset. */
PART_MERONYM,

/** The attribute a value expresses, or a value of this attribute. */
ATTRIBUTE,

/** A derivationally related form, typically across parts of speech. */
DERIVATIONALLY_RELATED,

/** An action entailed by this verb: snoring entails sleeping. */
ENTAILMENT,

/** The verb that entails this one; the inverse of {@link #ENTAILMENT}. */
ENTAILED_BY,

/** An effect this verb causes. */
CAUSE,

/** The cause of this verb; the inverse of {@link #CAUSE}. */
CAUSED_BY,

/** A related synset worth consulting. */
ALSO_SEE,

/** A verb sense grouped with this one. */
VERB_GROUP,

/** A satellite or head adjective in the same similarity cluster. */
SIMILAR_TO,

/** The verb an adjective is the participle of. */
PARTICIPLE,

/**
* The noun an adjective pertains to, or the adjective an adverb derives from. The source
* formats use one pointer for both directions of derivation, so this value does too.
*/
PERTAINYM,

/** The topical domain this synset belongs to. */
DOMAIN_TOPIC,

/** A synset belonging to this topical domain. */
MEMBER_OF_DOMAIN_TOPIC,

/** The regional domain this synset belongs to. */
DOMAIN_REGION,

/** A synset belonging to this regional domain. */
MEMBER_OF_DOMAIN_REGION,

/** The usage domain this synset belongs to, for example slang or archaism. */
DOMAIN_USAGE,

/** A synset belonging to this usage domain. */
MEMBER_OF_DOMAIN_USAGE
}
Loading
Loading