001    // This file is part of the Attempto Java Packages.
002    // Copyright 2008, 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.net.URI;
018    import java.net.URISyntaxException;
019    import java.util.ArrayList;
020    import java.util.List;
021    import java.util.Vector;
022    
023    import ch.uzh.ifi.attempto.ape.LexiconEntry;
024    
025    /**
026     * This class represents an ontology element which can be an individual ("constant"), a concept
027     * ("unary relation", "class", "type"), or a role ("binary relation", "property"). See the
028     * respective sub-classes.
029     *<p>
030     * In AceWiki, each ontology element corresponds to a word which has one or more word forms.
031     * Word forms are identified by a number (the word form id).
032     *<p>
033     * Every ontology element has an article which consists of a list of sentences.
034     * 
035     * @author Tobias Kuhn
036     */
037    public abstract class OntologyElement implements Comparable<OntologyElement> {
038            
039            private Ontology ontology;
040            
041            private final Vector<Sentence> text = new Vector<Sentence>();
042            private long id = -1;
043            
044            /**
045             * Initializes the ontology element.
046             */
047            protected OntologyElement() {
048            }
049            
050            /**
051             * Loads an ontology element from its serialized form.
052             * 
053             * @param serializedElement The serialized ontology element.
054             * @param id The id of the ontology element.
055             * @param ontology The ontology at which the ontology element should be registered.
056             * @return The ontology element.
057             */
058            static OntologyElement loadOntologyElement(String serializedElement, long id, Ontology ontology) {
059                    String[] lines = serializedElement.split("\n");
060                    if (!lines[0].startsWith("type:") || !lines[1].startsWith("words:")) {
061                            System.err.println("Cannot read ontology element " + id);
062                            return null;
063                    }
064                    String type = lines[0].substring("type:".length());
065                    String[] words = lines[1].substring("words:".length()).split(";");
066                    OntologyElement oe;
067                    if (type.equals("propername")) {
068                            oe = new Individual();
069                    } else if (type.equals("noun")) {
070                            oe = new NounConcept();
071                    } else if (type.equals("nounof")) {
072                            oe = new OfRole();
073                    } else if (type.equals("trverb")) {
074                            oe = new VerbRole();
075                    } else if (type.equals("tradj")) {
076                            oe = new TrAdjRole();
077                    } else {
078                            System.err.println("Cannot read ontology element " + id);
079                            return null;
080                    }
081                    oe.setId(id);
082                    oe.setWords(words);
083                    for (int i=2 ; i < lines.length ; i++) {
084                            Sentence sentence = Sentence.loadSentence(lines[i], oe);
085                            oe.text.add(sentence);
086                    }
087                    oe.ontology = ontology;
088                    ontology.register(oe);
089                    return oe;
090            }
091            
092            /**
093             * Returns the word forms. The position in the array corresponds to the word form id.
094             * 
095             * @return An array containing the word forms.
096             */
097            public abstract String[] getWords();
098            
099            /**
100             * Returns the word form for the given word form id.
101             * 
102             * @param n The word form id.
103             * @return The word form.
104             */
105            public String getWord(int n) {
106                    return getWords()[n];
107            }
108            
109            /**
110             * Returns the word form with the id 0 (the default word form).
111             * 
112             * @return The word form.
113             */
114            public String getWord() {
115                    return getWord(0);
116            }
117            
118            /**
119             * Returns the pretty-printed word form for the given word form id. The pretty-printing
120             * transforms underscores into blanks.
121             * 
122             * @param n The word form id.
123             * @return The word form.
124             */
125            public String getPrettyWord(int n) {
126                    String w = getWord(n);
127                    if (w == null) return null;
128                    return w.replace("_", " ");
129            }
130            
131            /**
132             * Sets the word forms. The order reflects the word form ids. The indexes of the
133             * ontology are automatically updated.
134             * 
135             * @param words The word forms.
136             */
137            public final void setWords(String... words) {
138                    if (ontology == null) {
139                            changeWords(words);
140                    } else {
141                            synchronized (ontology) {
142                                    ontology.removeFromWordIndex(this);
143                                    changeWords(words);
144                                    ontology.addToWordIndex(this);
145                                    ontology.refresh(this);
146                            }
147                    }
148            }
149            
150            /**
151             * Changes the word forms without updating the ontology indexes. The order reflects
152             * the word form ids.
153             * 
154             * @param words The word forms.
155             */
156            protected abstract void changeWords(String... words);
157            
158            /**
159             * Returns the headword that is used in the GUI to refer to this ontology element.
160             * For example, it is used for the title of the article. Unless overridden, it is
161             * the same as the pretty-printed word form with the id 0. 
162             * 
163             * @return The headword.
164             */
165            public String getHeadword() {
166                    return getPrettyWord(0);
167            }
168            
169            /**
170             * Returns the word type as it is shown to the user. Newer versions of AceWiki can
171             * savely change this value.
172             * 
173             * @return The word type.
174             */
175            public abstract String getType();
176            
177            /**
178             * Returns the word type as it is used internally. Changing this value in newer versions
179             * of AceWiki breaks backwards compatibility for loading ontologies.
180             * 
181             * @return The internal word type.
182             */
183            public abstract String getInternalType();
184            
185            /**
186             * Returns the ontology this ontology element is registered at.
187             * 
188             * @return The ontology.
189             */
190            public Ontology getOntology() {
191                    return ontology;
192            }
193            
194            /**
195             * Registers this ontology element at the given ontology. An ontology element can be
196             * registered only once.
197             * 
198             * @param ontology
199             */
200            public void registerAt(Ontology ontology) {
201                    if (this.ontology != null) {
202                            throw new RuntimeException("Cannot change the ontology for element " + toString());
203                    }
204                    if (ontology == null) {
205                            return;
206                    }
207                    this.ontology = ontology;
208                    synchronized (ontology) {
209                            ontology.register(this);
210                            ontology.save(this);
211                    }
212            }
213            
214            /**
215             * Returns the article text as a list of sentences.
216             * 
217             * @return The sentences.
218             */
219            public List<Sentence> getSentences() {
220                    return new ArrayList<Sentence>(text);
221            }
222            
223            /**
224             * Edits a sentence of the article. The old sentence is replaces by the new sentences.
225             * 
226             * @param oldSentence The sentence that should be edited.
227             * @param newSentences The new sentences.
228             * @return An integer value denoting the success/failure of the operation.
229             * @see Ontology#commitSentence(Sentence)
230             */
231            public int edit(Sentence oldSentence, List<Sentence> newSentences) {
232                    log("edit sentence of " + getWord() + ": " + oldSentence.getText() +
233                                    " > " + getSentencesString(newSentences));
234    
235                    synchronized (ontology) {
236                            if (text.contains(oldSentence)) {
237                                    int i = text.indexOf(oldSentence);
238                                    text.remove(i);
239                                    text.addAll(i, newSentences);
240                            } else {
241                                    log("error: sentence is not around anymore");
242                                    text.addAll(0, newSentences);
243                            }
244                            int success = 0;
245                            if (ontology != null) {
246                                    ontology.retractSentence(oldSentence);
247                                    for (Sentence s : newSentences) {
248                                            int successThis = ontology.commitSentence(s);
249                                            if (successThis > success) success = successThis;
250                                    }
251                                    ontology.save(this);
252                            }
253                            return success;
254                    }
255            }
256            
257            /**
258             * Adds one or more new sentences to the article. It has to be specified in front of which
259             * sentence the new sentences should be added.
260             * 
261             * @param followingSentence The sentence in front of which the new sentences should be added,
262             *   or null if the sentences should be added to the end of the article.
263             * @param newSentences The new sentences to be added.
264             * @return An integer value denoting the success/failure of the operation.
265             * @see Ontology#commitSentence(Sentence)
266             */
267            public int add(Sentence followingSentence, List<Sentence> newSentences) {
268                    log("add sentences of " + getWord() + ": " + getSentencesString(newSentences));
269    
270                    synchronized (ontology) {
271                            if (text.contains(followingSentence)) {
272                                    text.addAll(text.indexOf(followingSentence), newSentences);
273                            } else {
274                                    if (followingSentence != null) {
275                                            log("error: sentence is not around anymore");
276                                    }
277                                    text.addAll(newSentences);
278                            }
279                            int success = 0;
280                            if (ontology != null) {
281                                    for (Sentence s : newSentences) {
282                                            int successThis = ontology.commitSentence(s);
283                                            if (successThis > success) success = successThis;
284                                    }
285                                    ontology.save(this);
286                            }
287                            return success;
288                    }
289            }
290            
291            private String getSentencesString(List<Sentence> sentences) {
292                    String result = "";
293                    for (Sentence s : sentences) {
294                            result += s.getText() + " ";
295                    }
296                    return result;
297            }
298            
299            /**
300             * Removes the given sentence from the article.
301             * 
302             * @param sentence The sentence to be removed.
303             */
304            public void remove(Sentence sentence) {
305                    synchronized (ontology) {
306                            if (text.contains(sentence)) {
307                                    log("remove sentence: " + sentence.getText());
308                                    text.remove(sentence);
309                            }
310                            if (ontology != null) {
311                                    ontology.retractSentence(sentence);
312                                    ontology.save(this);
313                            }
314                    }
315            }
316            
317            /**
318             * Returns the lexicon entries (one for each word form).
319             * 
320             * @return The lexicon entries.
321             */
322            List<LexiconEntry> getLexiconEntries() {
323                    return null;
324            }
325            
326            /**
327             * Returns the URI of the ontology element. This URI is a concatenation of the
328             * ontology URI and the URI suffix of the ontology element.
329             * 
330             * @return The URI.
331             * @see #getURISuffix()
332             */
333            public final URI getURI() {
334                    String ontologyURI = "";
335                    if (ontology != null) {
336                            ontologyURI = ontology.getURI();
337                    }
338                    
339                    URI uri = null;
340                    try {
341                            uri = new URI(ontologyURI + getURISuffix());
342                    } catch (URISyntaxException ex) {
343                            ex.printStackTrace();
344                    }
345                    return uri;
346            }
347            
348            /**
349             * Returns the URI suffix of this ontology element. For example "#country".
350             * 
351             * @return The URI suffix.
352             * @see #getURI()
353             */
354            public String getURISuffix() {
355                    return "#" + getWord();
356            }
357            
358            final void setId(long id) {
359                    this.id = id;
360            }
361            
362            final long getId() {
363                    return id;
364            }
365            
366            /**
367             * Writes the text to the log file.
368             * 
369             * @param text The text to be written to the log file.
370             */
371            protected void log(String text) {
372                    if (ontology != null) {
373                            ontology.log(text);
374                    }
375            }
376            
377            /**
378             * Serializes this ontology element as a string.
379             * 
380             * @return The serialized ontology element.
381             */
382            String serialize() {
383                    String s = "type:";
384                    s += getInternalType() + "\nwords:";
385                    for (String word : getWords()) {
386                            if (word == null) {
387                                    s += ";";
388                            } else {
389                                    s += word + ";";
390                            }
391                    }
392                    s += "\n";
393                    for (Sentence sentence : text) {
394                            s += sentence.serialize();
395                    }
396                    return s;
397            }
398    
399            public int compareTo(OntologyElement e) {
400                    return getHeadword().toLowerCase().compareTo(e.getHeadword().toLowerCase());
401            }
402            
403            public String toString() {
404                    String l = "";
405                    for (String s : getWords()) {
406                            if (s == null) {
407                                    l += ",";
408                            } else {
409                                    l += s + ",";
410                            }
411                    }
412                    if (l.length() > 0) l = l.substring(0, l.length()-1);
413                    return getType() + "{" + l + "}";
414            }
415    
416    }