package edu.stanford.nlp.sempre.freebase.utils; /* * Distributed as part of WordWalk, a Java package for lexical * semantic relatedness using random graph walks. * * Copyright (C) 2008 Daniel Ramage * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 USA */ import java.io.File; import java.lang.ref.WeakReference; import java.util.*; import edu.stanford.nlp.io.RuntimeIOException; import edu.stanford.nlp.objectbank.ObjectBank; /** * <p>Fast, lightweight, constant time library for accessing WordNet. Use * one of the public load methods to create an instance by reading the WordNet * data files from disk. WordNet is represented with a set of WordID, * WordTagID, and SynsetID objects, each of which is unique within the WordNet * instance (so you can use == for equality checks). There are many types * of edges between these nodes (mostly SynsetID to SynsetID), where * each edge type is defined in the EdgeType enum.</p> * * <p>This class would be Serializable except that the default Java * serialization mechanism can't handle the depth of recursion between * the inner WordTagID objects -- hence an instance can be created only * by loading the requisite file from disk.</p> * * @author dramage * @author Chris Manning made minimal changes to make it JavaNLP land not Ramage land */ public final class WordNet { /** Global counter for assigning unique index to each loaded id. */ private final ArrayList<WordNetID> all = new ArrayList<WordNetID>(500000); /** Global immutable view of all */ private final List<WordNetID> immutableAll = Collections.unmodifiableList(all); /** Set of loaded edges types */ private final Set<EdgeType> loadedEdges = new HashSet<EdgeType>(); /** Indexes mapping canonical strings to their typed versions */ private final Map<String, SynsetID> synsets = new HashMap<String, SynsetID>(); private final Map<String, WordID> words = new HashMap<String, WordID>(); private final Map<String, WordTagID> wordtags = new HashMap<String, WordTagID>(); /** File resource backing this WordNet instance */ private final File path; /** * Private constructor - use one of the static constructor methods. * * @param path Path on disk to the underlying wordnet resource. */ private WordNet(File path) { this.path = path; } /** * All types of edges in wordnet. Some are transposes of each other, * as specified by parallel objects in edgeTransposePairs, * edgeTransposeTarget. */ public enum EdgeType { // structural relationships between words, wordtags, and synsets. etc WORD_TO_WORDTAG, WORDTAG_TO_WORD, // word to all wordtag in play WORDTAG_IN_SYNSET, SYNSET_HAS_WORDTAG, // wordtag to all synsets SYNSET_WORDTAGS_OVERLAP, // two synsets share wordtag // invertible synset relations HYPONYM, HYPERNYM, // from hyp; nouns and verbs INSTANCE_OF, HAS_INSTANCE, // from ins ENTAILS, ENTAILED_BY, // from ent; verbs only SIM_HEAD, SIM_SATELLITE, // from sim; adjectives only MM_HOLONYM, MM_MERONYM, // from mm; member meronyms MS_HOLONYM, MS_MERONYM, // from ms; substance meronym MP_HOLONYM, MP_MERONYM, // from mp; part meronym CAUSED_BY, CAUSES, // from cs; for verbs DERIVES, DERIVED_FROM, // from der; for noun to adj // self reflexive synset relations ATTRIBUTE, // from at // invertible word relations PARTICIPLE_OF, HAS_PARTICIPLE, // from ppl PERTAINS_TO, PERTANYM_OF, // from per SEE_ALSO_TO, SEE_ALSO_FROM, // from sa // self reflexive word relations ANTONYM, // from ant SIMILAR_VERBS, // from vgp // weird relations (words or synsets) from cls relation TERM_HAS_TOPIC, TOPIC_FROM_TERM, TERM_HAS_USAGE, USAGE_FROM_TERM, TERM_IN_REGION, REGION_HAS_TERM, } /** Set of relations that are transposes of eachother. */ private static final EdgeType[][] transpose = { {EdgeType.HYPONYM, EdgeType.HYPERNYM}, {EdgeType.INSTANCE_OF, EdgeType.HAS_INSTANCE}, {EdgeType.ENTAILS, EdgeType.ENTAILED_BY}, {EdgeType.SIM_HEAD, EdgeType.SIM_SATELLITE}, {EdgeType.MM_HOLONYM, EdgeType.MM_MERONYM}, {EdgeType.MS_HOLONYM, EdgeType.MS_MERONYM}, {EdgeType.MP_HOLONYM, EdgeType.MP_MERONYM}, {EdgeType.CAUSED_BY, EdgeType.CAUSES}, {EdgeType.DERIVES, EdgeType.DERIVED_FROM}, {EdgeType.PARTICIPLE_OF, EdgeType.HAS_PARTICIPLE}, {EdgeType.PERTAINS_TO, EdgeType.PERTANYM_OF}, {EdgeType.SEE_ALSO_TO, EdgeType.SEE_ALSO_FROM}, {EdgeType.TERM_HAS_TOPIC, EdgeType.TOPIC_FROM_TERM}, {EdgeType.TERM_HAS_USAGE, EdgeType.USAGE_FROM_TERM}, {EdgeType.TERM_IN_REGION, EdgeType.REGION_HAS_TERM} }; /** Set of relations that are self-reflexive, i.e. if a->b then b-> a */ private static final EdgeType[] reflexive = { EdgeType.SYNSET_WORDTAGS_OVERLAP, EdgeType.ATTRIBUTE, EdgeType.SIMILAR_VERBS, EdgeType.ANTONYM, }; /** * WordNet-defined part of speech tags. Folds adjective satellite * in with adjective. */ public enum PartOfSpeech { NOUN('n'), VERB('v'), ADJECTIVE('a'), ADVERB('r'); /** WordNet character for encoding the part of speech. */ public final char ssType; private PartOfSpeech(char ssType) { this.ssType = ssType; } public static PartOfSpeech fromWordNetSSType(char ssType) { for (PartOfSpeech pos : PartOfSpeech.values()) { if (pos.ssType == ssType) { return pos; } } if (ssType == 's') { return ADJECTIVE; } throw new IllegalArgumentException("Unexpected ss_type: " + ssType); } } /** Base of all wordnet ids. Comparison is based on index number. */ public abstract class WordNetID implements Comparable<WordNetID> { /** Globally unique index of this WordNetID */ private int index; /** Outgoing links from this node */ private Map<EdgeType, ArrayList<WordNetID>> links = new EnumMap<EdgeType, ArrayList<WordNetID>>(EdgeType.class); /** Private no-arg constructor prevents new subclasses */ private WordNetID() { index = all.size(); all.add(this); } /** * Returns the set of nodes linked from this note by the given * edge type. */ public List<WordNetID> get(EdgeType type) { List<WordNetID> set = links.get(type); if (set == null) { set = Collections.emptyList(); } return Collections.unmodifiableList(set); } /** Returns the index of this WordNetID in the enclosing WordNet instance */ public int index() { return index; } /** Adds a link with the given edge type to the object */ protected void add(EdgeType type, WordNetID target) { ArrayList<WordNetID> list = links.get(type); if (list == null) { list = new ArrayList<WordNetID>(); links.put(type, list); } if (!list.contains(target)) { list.add(target); } } /** Compacts and sorts each edge list. */ protected void compact() { for (ArrayList<WordNetID> edge : links.values()) { Collections.sort(edge); edge.trimToSize(); } } /** Ordered by index in enclosing WordNet */ public int compareTo(WordNetID other) { return this.index < other.index ? -1 : (this.index > other.index ? 1 : 0); } /** Returns the enclosing WordNet instance */ public WordNet getEnclosingWordNet() { return WordNet.this; } /** Ordered by index in enclosing WordNet */ @Override public boolean equals(Object other) { if (other == this) return true; if (!(other instanceof WordNetID)) return false; WordNetID w = (WordNetID) other; return this.index == w.index; } @Override public int hashCode() { return index; } } /** * Represents a word, e.g. "dog". * * Instances of this class are unique for a given {@link WordNet}, * so direct object equality and hashcode are the default behavior. * * @author dramage */ public final class WordID extends WordNetID { /** WordTags for this Word */ private final EnumMap<PartOfSpeech, WordTagID> mWordTags = new EnumMap<PartOfSpeech, WordTagID>(PartOfSpeech.class); /** The string of our word (with spaces converted to underscore) */ public final String word; /** Immutable view of all WordTagIDs this WordID is part of. */ public final Map<PartOfSpeech, WordTagID> wordTags = Collections.unmodifiableMap(mWordTags); private WordID(String word) { this.word = word; } /** * Gets the WordTagID for this word with the given part of speech * or null if the part of speech does not apply to this word. */ public WordTagID getWordTag(PartOfSpeech tag) { return mWordTags.get(tag); } /** Adds a reference to the given WordTagID */ private void addWordTag(WordTagID wordTagId) { assert !mWordTags.containsKey(wordTagId.tag) || mWordTags.get(wordTagId.tag) == wordTagId : "Unexpected duplicate WordTagID"; this.mWordTags.put(wordTagId.tag, wordTagId); } @Override public String toString() { return word; } } /** * Represents a word with part of speech tag, e.g. "dog#n". * * Instances of this class are unique for a given {@link WordNet}, * so direct object equality and hashcode are the default behavior. * * @author dramage */ public final class WordTagID extends WordNetID { /** Mutable view of word senses for this WordTag. */ private final ArrayList<SynsetID> mSynsets = new ArrayList<SynsetID>(1); /** The WordID we are an instance of */ public final WordID word; /** Our part of speech tag */ public PartOfSpeech tag; /** Immutable list of all synsets this WordTag takes part in. */ public final List<SynsetID> synsets = Collections.unmodifiableList(mSynsets); public List<SynsetID> getSynsets() { return this.synsets; } private WordTagID(WordID word, PartOfSpeech tag) { this.word = word; this.tag = tag; } /** * Gets the n'th synset associated with this word tag. This index * is 1-based to be consistent with wordnet's numbering scheme; i.e. * getSynset(0) will throw an IllegalArgumentException. * */ public SynsetID getSynset(int num) { num--; // convert to 0-indexed if (num < 0) { throw new IllegalArgumentException("SynsetIDs are 1-based"); } return mSynsets.get(num); } /** Adds the given Synset into this WordTag with the given number */ private void addSynset(SynsetID synsetId, int num) { num--; // convert to 0-indexed assert num >= 0 && (mSynsets.size() <= num || mSynsets.get(num) == null || mSynsets.get(num) == synsetId) : "Unexpected repeat of word sense " + synsetId + " on " + this + ": already " + mSynsets.get(num); while (mSynsets.size() <= num) { mSynsets.add(null); } mSynsets.set(num, synsetId); } @Override public String toString() { return word + "#" + tag.ssType; } } /** * Represents a WordNet synset id as an int, e.g. 301380127. * * Instances of this class are unique for a given {@link WordNet}, * so direct object equality and hashcode are the default behavior. * * Each SynsetID has potentially many WordTagSenseID's. * * @author dramage */ public final class SynsetID extends WordNetID { /** Modifiable WordTagIDs associated with this SynsetID */ private final ArrayList<WordTagID> mWordTags = new ArrayList<WordTagID>(1); /** The synset id number */ public final int synset; /** Number of times this sense was tagged in a corpus or 0 for not seen. */ public final int count; /** The number of this synset in the first WordTagID (for toString) */ private int numberInFirstWordTagID = 0; /** All WordTags that this Synset is a part of */ public final List<WordTagID> wordtags = Collections.unmodifiableList(mWordTags); private SynsetID(int synset, int count) { this.synset = synset; this.count = count; } /** Returns the n'th word sense */ public WordTagID getWordTag(int num) { num--; // convert to 0-indexed if (num < 0) { throw new IllegalArgumentException("WordTagIDs are 1-based"); } return mWordTags.get(num); } /** * Adds the given WordTagSense into this synset */ private void addWordSense(WordTagID wordTagId, int num) { num--; // convert to 0-indexed assert num >= 0 && (mWordTags.size() <= num || mWordTags.get(num) == null || mWordTags.get(num) == wordTagId) : "Unexpected repeat of word " + wordTagId + " on " + this + ": already " + mWordTags.get(num); while (mWordTags.size() <= num) { mWordTags.add(null); } mWordTags.set(num, wordTagId); } @Override public String toString() { return mWordTags.get(0) + "#" + numberInFirstWordTagID; } } /** Returns a collection of all the WordNetIDs loaded. */ public Collection<WordNetID> getAllWordNetIDs() { return immutableAll; } /** Returns a collection of all the words loaded. */ public Collection<String> getAllWords() { return words.keySet(); } /** * Returns the given WordNetID by it's index. The return value could * be cast to one of the three possible subclasses: WordID, WordTagID, * or SynsetID. */ public WordNetID getWordNetID(int index) { return all.get(index); } /** * Returns the WordNetID referred to by the given string. Delegates * to one of getSynsetID, getWordTagID, or getWordID depending on the * format of the string or null if no such ID can be found. */ public WordNetID getWordNetID(String string) { int firstHash = string.indexOf('#'); if (firstHash < 0) { WordNetID rv = getWordID(string); return rv != null ? rv : getSynsetID(string); } else { int secondHash = string.lastIndexOf('#'); if (firstHash == secondHash) { return getWordTagID(string); } else { return getSynsetID(string); } } } /** * Returns the SynsetID instance as described by the given id string * or null if there is no such id. The string can either be a synset id * such as "100001740" or a word sense such as "entity#n#1". */ public SynsetID getSynsetID(String string) { // check if specified as "100001740" SynsetID id = synsets.get(string); if (id == null) { // check if specified as "entity#n#1" int split = string.lastIndexOf('#'); if (split >= 1) { int num; try { num = Integer.parseInt(string.substring(split + 1)); } catch (NumberFormatException e) { return null; } WordTagID wordTagId = wordtags.get(string.substring(0, split)); if (wordTagId != null) { id = wordTagId.getSynset(num); } } } return id; } /** * Returns the WordID instance from a string such as "dog" * or null if there is no such id. */ public WordID getWordID(String string) { return words.get(string); } /** * Returns the WordTagID instance from a string such as "dog#n" * or null if there is no such id. */ public WordTagID getWordTagID(String string) { return wordtags.get(string); } /** * The total number of loaded nodes in the WordNet instance. */ public int size() { return all.size(); } /** * Returns the path on disk of the underlying WordNet database. */ @Override public String toString() { return path.toString(); } /** * Stitches together the structural edge types, WORD_TOWORDTAG, * SYNSET_HAS_WORDTAG, etc. */ private void createStructuralEdges() { // create WORD_TO_WORDTAG and WORDTAG_TO_WORD for (WordID word : words.values()) { for (WordTagID wordtag : word.wordTags.values()) { word.add(EdgeType.WORD_TO_WORDTAG, wordtag); wordtag.add(EdgeType.WORDTAG_TO_WORD, word); } } // create WORTAG_IN_SYNSET and SYNSET_HAS_WORDTAG for (WordTagID wordtag : wordtags.values()) { for (SynsetID synset : wordtag.synsets) { wordtag.add(EdgeType.WORDTAG_IN_SYNSET, synset); synset.add(EdgeType.SYNSET_HAS_WORDTAG, wordtag); } } // create SYNSET_WORDTAGS_OVERLAP for (WordTagID wordtag : wordtags.values()) { for (SynsetID synsetA : wordtag.mSynsets) { if (synsetA == null) { continue; } for (SynsetID synsetB : wordtag.mSynsets) { if (synsetB == null || synsetA == synsetB) { continue; } synsetA.add(EdgeType.SYNSET_WORDTAGS_OVERLAP, synsetB); } } } // record that we have created these edges loadedEdges.addAll(Arrays.asList(new EdgeType[]{ EdgeType.WORD_TO_WORDTAG, EdgeType.WORDTAG_TO_WORD, EdgeType.WORDTAG_IN_SYNSET, EdgeType.SYNSET_HAS_WORDTAG, EdgeType.SYNSET_WORDTAGS_OVERLAP, })); } /** Compacts and finalizes ordering of all data structures */ private void compact() { // sort the WordNetID's by type (then by order added) Collections.sort(all, new Comparator<WordNetID>() { public int compare(WordNetID o1, WordNetID o2) { if (o1.getClass() == o2.getClass()) { return o1.compareTo(o2); } else if (o1.getClass() == SynsetID.class) { return -1; } else if (o2.getClass() == SynsetID.class) { return 1; } else if (o1.getClass() == WordTagID.class) { return -1; } else if (o2.getClass() == WordTagID.class) { return 1; } else { throw new RuntimeException("Unexpected WordNetID type"); } } }); // re-number the wordnet id's for (int i = 0; i < all.size(); i++) { all.get(i).index = i; } for (WordNetID id : all) { id.compact(); } for (SynsetID id : synsets.values()) { id.mWordTags.trimToSize(); } for (WordTagID id : wordtags.values()) { id.mSynsets.trimToSize(); } all.trimToSize(); } /** Checks the representation invariants */ private void checkrep() { for (EdgeType edgetype : EdgeType.values()) { assertIt(loadedEdges.contains(edgetype), "Failed to load " + edgetype); } for (SynsetID synset : synsets.values()) { assertIt(all.get(synset.index()) == synset, "Misplaced synset " + synset); assertIt(synset.getWordTag(1).getSynset(synset.numberInFirstWordTagID) == synset, "Wrong number in first WordTagID " + synset); assertIt(!synset.wordtags.contains(null), synset + " contains null WordTagID"); for (WordTagID wordtag : synset.wordtags) { assertIt(wordtag.synsets.contains(synset), "Miswired Synset " + synset); } } for (WordTagID wordtag : wordtags.values()) { assertIt(all.get(wordtag.index()) == wordtag, "Misplaced wordtag " + wordtag); assertIt(!wordtag.synsets.contains(null), wordtag + " contains null SynsetID"); for (SynsetID synset : wordtag.synsets) { assertIt(synset.wordtags.contains(wordtag), "Miswired WordTag " + wordtag); } assertIt(wordtag.word.wordTags.containsValue(wordtag), "Miswired WordTag " + wordtag); } for (WordID word : words.values()) { assertIt(all.get(word.index()) == word, "Misplaced word " + word); for (WordTagID wordtag : word.wordTags.values()) { assertIt(wordtag.word == word, "Miswired WordTag " + word); } } for (WordNetID id1 : all) { for (EdgeType[] pair : transpose) { for (WordNetID id2 : id1.get(pair[0])) { assertIt(id2.get(pair[1]).contains(id1), "Missing transpose " + Arrays.asList(transpose) + " " + id1 + " " + id2); } } } for (WordNetID id1 : all) { for (EdgeType type : reflexive) { for (WordNetID id2 : id1.get(type)) { assertIt(id2.get(type).contains(id1), "Missing reflextive " + type + " " + id1 + " " + id2); } } } } /** Assertion method used by checkRep */ private static void assertIt(boolean condition, String message) { if (!condition) { throw new AssertionError(message); } } // // WordNet loader // /** Weak collection of WordNet instances based on file name */ private static final Collection<WeakReference<WordNet>> instances = new LinkedList<WeakReference<WordNet>>(); /** * Loads an instance of WordNet from the given WordNet database -- * currently supports only Prolog DB format. */ public static WordNet load(File path) { System.err.println("WordNet.load: " + path); // see if already loaded for (Iterator<WeakReference<WordNet>> it = instances.iterator(); it.hasNext();) { WordNet wordnet = it.next().get(); if (wordnet == null) { it.remove(); } else if (wordnet.path.equals(path)) { return wordnet; } } // not already loaded, load now try { WordNet wordnet = loadPrologWordNet(path); instances.add(new WeakReference<WordNet>(wordnet)); return wordnet; } catch (RuntimeIOException e) { throw new IllegalArgumentException( "Provided path not a valid WordNet PrologDB directory", e); } } /** * Returns an instance of WordNet based on the contents of the WordNet * databases as stored in the given path prolog WordNet 3.0 format. */ public static WordNet loadPrologWordNet(File path) { WordNet wordnet = new WordNet(path); /** Global stashing point for unassigned word senses depending on wn POS tag */ Map<WordTagID, SynsetID> deferredPositionSynsets = new HashMap<WordTagID, SynsetID>(); // // read the synsets file // { for (String line : ObjectBank.getLineIterator(new File(path, "wn_s.pl"))) { if (line.length() == 0) continue; // fields from the line String[] fields = line.substring(2, line.length() - 2).split(","); final int wordTagNumberInSynset = Integer.parseInt(fields[1]); final int synsetNumberInWordTag = Integer.parseInt( fields.length > 4 ? fields[4] : "0"); final int senseCount = Integer.parseInt( fields.length > 5 ? fields[5] : "0"); final String word = new String(fields[2].substring( 1, fields[2].length() - 1).replaceAll("\\s+", "_")) .toLowerCase(); final PartOfSpeech tag = PartOfSpeech.fromWordNetSSType( fields[3].charAt(0)); final String wordTag = word + "#" + tag.ssType; final String synset = fields[0]; // add WordID WordID wordId = wordnet.words.get(word); if (wordId == null) { wordId = wordnet.new WordID(word); wordnet.words.put(word, wordId); } // add WordTagID WordTagID wordTagId = wordnet.wordtags.get(wordTag); if (wordTagId == null) { wordTagId = wordnet.new WordTagID(wordId, tag); wordnet.wordtags.put(wordTag, wordTagId); } // add SynsetID SynsetID synsetId = wordnet.synsets.get(synset); if (synsetId == null) { synsetId = wordnet.new SynsetID(Integer.parseInt(synset), senseCount); wordnet.synsets.put(synset, synsetId); } // link WordID to WordTagID wordId.addWordTag(wordTagId); // link WordTagID to SynsetID if (synsetNumberInWordTag == 0) { if (deferredPositionSynsets.containsKey(wordTagId)) { throw new RuntimeException("Error: don't know what " + "to do when more than one synset doesn't come" + " with a valid sense number"); } else { deferredPositionSynsets.put(wordTagId, synsetId); } } else { wordTagId.addSynset(synsetId, synsetNumberInWordTag); } synsetId.addWordSense(wordTagId, wordTagNumberInSynset); } // add in deferredPositionSynsets for (Map.Entry<WordTagID, SynsetID> entry : deferredPositionSynsets.entrySet()) { boolean placed = false; for (int i = 0; i < entry.getKey().mSynsets.size(); i++) { if (entry.getKey().mSynsets.get(i) == null) { entry.getKey().mSynsets.set(i, entry.getValue()); placed = true; } } if (!placed) { throw new AssertionError("Unable to place deferred synset"); } } // tell each SynsetID its position in its first WordTagID for (SynsetID synset : wordnet.synsets.values()) { int position = synset.wordtags.get(0).mSynsets.indexOf(synset) + 1; assert position >= 1 : "Unexpected: couldn't find the synset"; synset.numberInFirstWordTagID = position; } // add all structural edges wordnet.createStructuralEdges(); } // // read all synset relations defined over SynsetID pairs // // invert-ready synset relations wordnet.loadSynsetRelation(path, "hyp", EdgeType.HYPONYM); wordnet.loadSynsetRelation(path, "ins", EdgeType.INSTANCE_OF); wordnet.loadSynsetRelation(path, "ent", EdgeType.ENTAILS); wordnet.loadSynsetRelation(path, "sim", EdgeType.SIM_HEAD); wordnet.loadSynsetRelation(path, "mm", EdgeType.MM_HOLONYM); wordnet.loadSynsetRelation(path, "ms", EdgeType.MS_HOLONYM); wordnet.loadSynsetRelation(path, "mp", EdgeType.MP_HOLONYM); wordnet.loadSynsetRelation(path, "cs", EdgeType.CAUSED_BY); // self-reflexive synset relations wordnet.loadSynsetRelation(path, "at", EdgeType.ATTRIBUTE); // invert-ready word relations wordnet.loadWordRelation(path, "ppl", EdgeType.PARTICIPLE_OF); wordnet.loadWordRelation(path, "per", EdgeType.PERTAINS_TO); wordnet.loadWordRelation(path, "sa", EdgeType.SEE_ALSO_TO); // self-reflexive word relations wordnet.loadWordRelation(path, "der", EdgeType.DERIVES); wordnet.loadWordRelation(path, "vgp", EdgeType.SIMILAR_VERBS); wordnet.loadWordRelation(path, "ant", EdgeType.ANTONYM); // weird class relations wordnet.loadCLSRelations(path); // do transposes for (EdgeType[] pair : transpose) { wordnet.addTranspose(pair[0], pair[1]); } // compact data structure and check rep invariants wordnet.compact(); wordnet.checkrep(); return wordnet; } /** * Loads the given relation from the prolog file, storing the result in * the given EdgeType. */ private void loadSynsetRelation(File path, String relation, EdgeType type) { if (loadedEdges.contains(type)) { throw new IllegalArgumentException("Unexpected error: trying to load " + type + " twice"); } loadedEdges.add(type); for (String line : ObjectBank.getLineIterator(new File(path, "wn_" + relation + ".pl"))) { if (line.length() == 0) continue; String[] fields = line.substring(relation.length() + 1, line.length() - 2).split(","); SynsetID id1 = getSynsetID(fields[0]); SynsetID id2 = getSynsetID(fields[1]); id1.add(type, id2); } } /** * Loads the given relation from the prolog file, storing the result in * the given EdgeType. */ private void loadWordRelation(File path, String relation, EdgeType type) { if (loadedEdges.contains(type)) { throw new IllegalArgumentException("Unexpected error: trying to load " + type + " twice"); } loadedEdges.add(type); for (String line : ObjectBank.getLineIterator(new File(path, "wn_" + relation + ".pl"))) { if (line.length() == 0) continue; String[] fields = line.substring(relation.length() + 1, line.length() - 2).split(","); final SynsetID sid1 = getSynsetID(fields[0]); final SynsetID sid2 = getSynsetID(fields[2]); if (sid1 == sid2) { System.err.println("WordNet.loadWordRelation(" + relation + "): skipping self-loop on " + sid1); } else { sid1.add(type, sid2); } } } /** * Loads the given relation from the prolog file, storing the result in * the given EdgeType. */ private void loadCLSRelations(File path) { final String relation = "cls"; if (loadedEdges.contains(EdgeType.TERM_HAS_TOPIC) || loadedEdges.contains(EdgeType.TERM_HAS_USAGE) || loadedEdges.contains(EdgeType.TERM_IN_REGION)) { throw new IllegalArgumentException("Unexpected error while loading " + relation); } loadedEdges.add(EdgeType.TERM_HAS_TOPIC); loadedEdges.add(EdgeType.TERM_HAS_USAGE); loadedEdges.add(EdgeType.TERM_IN_REGION); for (String line : ObjectBank.getLineIterator(new File(path, "wn_" + relation + ".pl"))) { if (line.length() == 0) continue; String[] fields = line.substring(relation.length() + 1, line.length() - 2).split(","); if (fields.length != 5 || fields[4].length() != 1) { throw new IllegalArgumentException("Badly formed file for " + relation); } SynsetID sid1 = getSynsetID(fields[0]); SynsetID sid2 = getSynsetID(fields[2]); int num1 = Integer.parseInt(fields[1]); int num2 = Integer.parseInt(fields[3]); assert !((num1 == 0) ^ (num2 == 0)); WordNetID id1 = num1 == 0 ? sid1 : sid1.getWordTag(num1); WordNetID id2 = num2 == 0 ? sid2 : sid2.getWordTag(num2); switch (fields[4].charAt(0)) { case 't': id1.add(EdgeType.TERM_HAS_TOPIC, id2); break; case 'u': id1.add(EdgeType.TERM_HAS_USAGE, id2); break; case 'r': id1.add(EdgeType.TERM_IN_REGION, id2); break; default: throw new IllegalArgumentException("Unexpected relation type " + fields[4]); } } } /** Adds a new edge type t2 as the transpose of t1 */ private void addTranspose(EdgeType t1, EdgeType t2) { if (!loadedEdges.contains(t1)) { throw new IllegalArgumentException("Cannot transpose: doesn't contain " + t1); } else if (loadedEdges.contains(t2)) { throw new IllegalArgumentException("Cannot transpose: already contains " + t2); } loadedEdges.add(t2); /** Add reverse links for all types */ for (WordNetID ab : all) { for (WordNetID ba : ab.get(t1)) { ba.add(t2, ab); } } } // // Sample main method for testing // public static void main(String[] args) { WordNet wordnet = load(new File(args[0])); SynsetID id1 = wordnet.getSynsetID("run#v#1"); for (EdgeType edgetype : EdgeType.values()) { for (WordNetID id2 : id1.get(edgetype)) { System.out.println(edgetype + " " + id2); } } System.out.println(wordnet.getAllWordNetIDs().size()); for (SynsetID synset : wordnet.synsets.values()) { System.out.printf("%06d %s\n", synset.synset, synset.toString()); } } }