001    // This file is part of the Attempto Java Packages.
002    // Copyright 2008-2009, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch).
003    //
004    // The Attempto Java Packages is free software: you can redistribute it and/or modify it under the
005    // terms of the GNU Lesser General Public License as published by the Free Software Foundation,
006    // either version 3 of the License, or (at your option) any later version.
007    //
008    // The Attempto Java Packages is distributed in the hope that it will be useful, but WITHOUT ANY
009    // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
010    // PURPOSE. See the GNU Lesser General Public License for more details.
011    //
012    // You should have received a copy of the GNU Lesser General Public License along with the Attempto
013    // Java Packages. If not, see http://www.gnu.org/licenses/.
014    
015    package ch.uzh.ifi.attempto.acewiki.core.ontology;
016    
017    import java.util.ArrayList;
018    import java.util.List;
019    
020    import ch.uzh.ifi.attempto.ape.Gender;
021    import ch.uzh.ifi.attempto.ape.LexiconEntry;
022    
023    /**
024     * This class represents an individual (in logic called "constant"). AceWiki supports only
025     * proper names to represent individuals (and no extensions are planned in this respect).
026     * For that reason, this class is proper name specific.
027     *<p>
028     * Proper names can be used either with a definite article (e.g. "the United Nations") or without
029     * (e.g. "Switzerland"). Furthermore, proper names can have an abbreviation that is a shorter
030     * name with exactly the same meaning. This abbreviation can aswell be used either with a definite
031     * article (e.g. "the UN") or without (e.g. "ACE").
032     *<p>
033     * Proper names have four word forms. The first one is the proper name with the definite
034     * article or just the proper name if no definite article is used for this proper name. The second
035     * one is in each case just the bare proper name. The third form is the abbreviation with the
036     * definite article if there is one. The fourth form, finally, is just the bare abbreviation. If
037     * there is no abbreviation then the third and fourth form are identical to the first and second
038     * form, respectively. For proper names that do not use a definite article and that have no
039     * abbreviation, all four forms are identical.
040     *<p>
041     * 0: proper name, preceded by "the" if used with definite article.
042     * 1: bare proper name.
043     * 2: abbreviation, preceded by "the" if used with definite article; or the same as 0 if there is
044     *    no abbreviation.
045     * 3: bare abbreviation; or the same as 1 if there is no abbreviation.
046     *<p>
047     * Examples: ["the United Nations", "United Nations", "the UN", "UN"];
048     *           ["the Nile", "Nile", "Nile", "Nile];
049     *           ["Switzerland", "Switzerland", "Switzerland", "Switzerland"];
050     *           ["Attempto Controlled English", "Attempto Controlled English", "ACE", "ACE"].
051     * 
052     * @author Tobias Kuhn
053     */
054    public class Individual extends OntologyElement {
055            
056            private String word, abbrev;
057            private boolean wordDefArt, abbrevDefArt;
058            
059            private List<Concept> conceptsCache;
060            private long conceptsCacheStateID = -1;
061            
062            /**
063             * Creates a new individual that has no name yet and is not registered to an ontology.
064             */
065            public Individual() {
066                    this.word = "";
067                    this.abbrev = null;
068                    this.wordDefArt = false;
069                    this.abbrevDefArt = false;
070            }
071            
072            public String[] getWords() {
073                    if (abbrev == null) {
074                            if (wordDefArt) {
075                                    return new String[] {"the " + word, word, "the " + word, word};
076                            } else {
077                                    return new String[] {word, word, word, word};
078                            }
079                    } else {
080                            if (wordDefArt) {
081                                    if (abbrevDefArt) {
082                                            return new String[] {"the " + word, word, "the " + abbrev, abbrev};
083                                    } else {
084                                            return new String[] {"the " + word, word, abbrev, abbrev};
085                                    }
086                            } else {
087                                    if (abbrevDefArt) {
088                                            return new String[] {word, word, "the " + abbrev, abbrev};
089                                    } else {
090                                            return new String[] {word, word, abbrev, abbrev};
091                                    }
092                            }
093                    }
094            }
095            
096            public String getHeadword() {
097                    if (abbrev == null) {
098                            return getPrettyWord(1);
099                    } else {
100                            return getPrettyWord(1) + " (" + getPrettyWord(3) + ")";
101                    }
102            }
103            
104            public String[] getIndexEntries() {
105                    return new String[] {getHeadword(), getPrettyWord(3)};
106            }
107            
108            protected void changeWords(String... words) {
109                    if (words.length == 1) {
110                            word = words[0];
111                            wordDefArt = false;
112                            abbrev = null;
113                            abbrevDefArt = false;
114                    } else if (words.length == 2) {
115                            word = words[1];
116                            wordDefArt = words[0].startsWith("the ");
117                            abbrev = null;
118                            abbrevDefArt = false;
119                    } else if (words[2] == null || words[0].equals(words[2])) {
120                            word = words[1];
121                            wordDefArt = words[0].startsWith("the ");
122                            abbrev = null;
123                            abbrevDefArt = false;
124                    } else {
125                            word = words[1];
126                            wordDefArt = words[0].startsWith("the ");
127                            abbrev = words[3];
128                            abbrevDefArt = words[2].startsWith("the ");
129                    }
130            }
131    
132            List<LexiconEntry> getLexiconEntries() {
133                    List<LexiconEntry> entries = new ArrayList<LexiconEntry>();
134                    if (wordDefArt) {
135                            entries.add(LexiconEntry.createPropernameDefSgEntry(word, word, Gender.NEUTRAL));
136                    } else {
137                            entries.add(LexiconEntry.createPropernameSgEntry(word, word, Gender.NEUTRAL));
138                    }
139                    if (abbrev != null) {
140                            if (abbrevDefArt) {
141                                    entries.add(LexiconEntry.createPropernameDefSgEntry(abbrev, word, Gender.NEUTRAL));
142                            } else {
143                                    entries.add(LexiconEntry.createPropernameSgEntry(abbrev, word, Gender.NEUTRAL));
144                            }
145                    }
146                    return entries;
147            }
148            
149            public String getType() {
150                    return "Proper Name";
151            }
152            
153            public String getInternalType() {
154                    return "propername";
155            }
156            
157            /**
158             * Returns true if the proper name has to be used with the definite article "the".
159             * 
160             * @return true if the definite article "the" has to be used.
161             */
162            public boolean hasDefiniteArticle() {
163                    return wordDefArt;
164            }
165            
166            /**
167             * Returns true if the given word form uses the definite article "the". This returns
168             * always false for 1 and 3.
169             * 
170             * @param wordNumber the word number
171             * @return true if the definite article "the" is used for the word form of the
172             *         given word number
173             */
174            public boolean hasDefiniteArticle(int wordNumber) {
175                    if (wordNumber == 0) {
176                            return wordDefArt;
177                    } else if (wordNumber == 2) {
178                            return abbrevDefArt;
179                    } else {
180                            return false;
181                    }
182            }
183            
184            /**
185             * Returns the abbreviation (without definite article) or null if there is no abbreviation.
186             * 
187             * @return the abbreviation
188             */
189            public String getAbbreviation() {
190                    if (abbrev == null) return null;
191                    return abbrev.replace("_", " ");
192            }
193            
194            public String getURISuffix() {
195                    return "#" + word;
196            }
197            
198            /**
199             * Calculates all concepts this individual belongs to.
200             * 
201             * @return A list of all concepts of this individual.
202             * @see Ontology#getConcepts(Individual)
203             */
204            public synchronized List<Concept> getConcepts() {
205                    Ontology o = getOntology();
206                    if (conceptsCacheStateID != o.getStateID()) {
207                            conceptsCache = o.getConcepts(this);
208                            conceptsCacheStateID = o.getStateID();
209                    }
210                    return new ArrayList<Concept>(conceptsCache);
211            }
212            
213            /**
214             * Returns the cached concepts or null if there are no cached concepts. The returned
215             * concepts might not be up-to-date.
216             * 
217             * @return A list of the cached concepts of this individual.
218             */
219            public List<Concept> getCachedConcepts() {
220                    if (conceptsCache == null) return null;
221                    return new ArrayList<Concept>(conceptsCache);
222            }
223    
224            /**
225             * Returns true if the concepts of this individual are cached and up-to-date and thus
226             * do not have to be recalculated.
227             * 
228             * @return true if the concepts are cached.
229             */
230            public boolean areConceptsCached() {
231                    return conceptsCacheStateID == getOntology().getStateID();
232            }
233    
234    }