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 a list of words that should be listed in the index to point to this ontology
171             * element.
172             * 
173             * @return The index words.
174             */
175            public String[] getIndexEntries() {
176                    return new String[] {getHeadword()};
177            }
178            
179            /**
180             * Returns the word type as it is shown to the user. Newer versions of AceWiki can
181             * savely change this value.
182             * 
183             * @return The word type.
184             */
185            public abstract String getType();
186            
187            /**
188             * Returns the word type as it is used internally. Changing this value in newer versions
189             * of AceWiki breaks backwards compatibility for loading ontologies.
190             * 
191             * @return The internal word type.
192             */
193            public abstract String getInternalType();
194            
195            /**
196             * Returns the ontology this ontology element is registered at.
197             * 
198             * @return The ontology.
199             */
200            public Ontology getOntology() {
201                    return ontology;
202            }
203            
204            /**
205             * Registers this ontology element at the given ontology. An ontology element can be
206             * registered only once.
207             * 
208             * @param ontology
209             */
210            public void registerAt(Ontology ontology) {
211                    if (this.ontology != null) {
212                            throw new RuntimeException("Cannot change the ontology for element " + toString());
213                    }
214                    if (ontology == null) {
215                            return;
216                    }
217                    this.ontology = ontology;
218                    synchronized (ontology) {
219                            ontology.register(this);
220                            ontology.save(this);
221                    }
222            }
223            
224            /**
225             * Returns the article text as a list of sentences.
226             * 
227             * @return The sentences.
228             */
229            public List<Sentence> getSentences() {
230                    return new ArrayList<Sentence>(text);
231            }
232            
233            /**
234             * Edits a sentence of the article. The old sentence is replaces by the new sentences.
235             * 
236             * @param oldSentence The sentence that should be edited.
237             * @param newSentences The new sentences.
238             * @return An integer value denoting the success/failure of the operation.
239             * @see Ontology#commitSentence(Sentence)
240             */
241            public int edit(Sentence oldSentence, List<Sentence> newSentences) {
242                    log("edit sentence of " + getWord() + ": " + oldSentence.getText() +
243                                    " > " + getSentencesString(newSentences));
244    
245                    synchronized (ontology) {
246                            if (text.contains(oldSentence)) {
247                                    int i = text.indexOf(oldSentence);
248                                    text.remove(i);
249                                    text.addAll(i, newSentences);
250                            } else {
251                                    log("error: sentence is not around anymore");
252                                    text.addAll(0, newSentences);
253                            }
254                            int success = 0;
255                            if (ontology != null) {
256                                    ontology.retractSentence(oldSentence);
257                                    for (Sentence s : newSentences) {
258                                            int successThis = ontology.commitSentence(s);
259                                            if (successThis > success) success = successThis;
260                                    }
261                                    ontology.save(this);
262                            }
263                            return success;
264                    }
265            }
266            
267            /**
268             * Adds one or more new sentences to the article. It has to be specified in front of which
269             * sentence the new sentences should be added.
270             * 
271             * @param followingSentence The sentence in front of which the new sentences should be added,
272             *   or null if the sentences should be added to the end of the article.
273             * @param newSentences The new sentences to be added.
274             * @return An integer value denoting the success/failure of the operation.
275             * @see Ontology#commitSentence(Sentence)
276             */
277            public int add(Sentence followingSentence, List<Sentence> newSentences) {
278                    log("add sentences of " + getWord() + ": " + getSentencesString(newSentences));
279    
280                    synchronized (ontology) {
281                            if (text.contains(followingSentence)) {
282                                    text.addAll(text.indexOf(followingSentence), newSentences);
283                            } else {
284                                    if (followingSentence != null) {
285                                            log("error: sentence is not around anymore");
286                                    }
287                                    text.addAll(newSentences);
288                            }
289                            int success = 0;
290                            if (ontology != null) {
291                                    for (Sentence s : newSentences) {
292                                            int successThis = ontology.commitSentence(s);
293                                            if (successThis > success) success = successThis;
294                                    }
295                                    ontology.save(this);
296                            }
297                            return success;
298                    }
299            }
300            
301            private String getSentencesString(List<Sentence> sentences) {
302                    String result = "";
303                    for (Sentence s : sentences) {
304                            result += s.getText() + " ";
305                    }
306                    return result;
307            }
308            
309            /**
310             * Removes the given sentence from the article.
311             * 
312             * @param sentence The sentence to be removed.
313             */
314            public void remove(Sentence sentence) {
315                    synchronized (ontology) {
316                            if (text.contains(sentence)) {
317                                    log("remove sentence: " + sentence.getText());
318                                    text.remove(sentence);
319                            }
320                            if (ontology != null) {
321                                    ontology.retractSentence(sentence);
322                                    ontology.save(this);
323                            }
324                    }
325            }
326            
327            /**
328             * Returns the lexicon entries (one for each word form).
329             * 
330             * @return The lexicon entries.
331             */
332            List<LexiconEntry> getLexiconEntries() {
333                    return null;
334            }
335            
336            /**
337             * Returns the URI of the ontology element. This URI is a concatenation of the
338             * ontology URI and the URI suffix of the ontology element.
339             * 
340             * @return The URI.
341             * @see #getURISuffix()
342             */
343            public final URI getURI() {
344                    String ontologyURI = "";
345                    if (ontology != null) {
346                            ontologyURI = ontology.getURI();
347                    }
348                    
349                    URI uri = null;
350                    try {
351                            uri = new URI(ontologyURI + getURISuffix());
352                    } catch (URISyntaxException ex) {
353                            ex.printStackTrace();
354                    }
355                    return uri;
356            }
357            
358            /**
359             * Returns the URI suffix of this ontology element. For example "#country".
360             * 
361             * @return The URI suffix.
362             * @see #getURI()
363             */
364            public String getURISuffix() {
365                    return "#" + getWord();
366            }
367            
368            final void setId(long id) {
369                    this.id = id;
370            }
371            
372            final long getId() {
373                    return id;
374            }
375            
376            /**
377             * Writes the text to the log file.
378             * 
379             * @param text The text to be written to the log file.
380             */
381            protected void log(String text) {
382                    if (ontology != null) {
383                            ontology.log(text);
384                    }
385            }
386            
387            /**
388             * Serializes this ontology element as a string.
389             * 
390             * @return The serialized ontology element.
391             */
392            String serialize() {
393                    String s = "type:";
394                    s += getInternalType() + "\nwords:";
395                    for (String word : getWords()) {
396                            if (word == null) {
397                                    s += ";";
398                            } else {
399                                    s += word + ";";
400                            }
401                    }
402                    s += "\n";
403                    for (Sentence sentence : text) {
404                            s += sentence.serialize();
405                    }
406                    return s;
407            }
408    
409            public int compareTo(OntologyElement e) {
410                    return getHeadword().toLowerCase().compareTo(e.getHeadword().toLowerCase());
411            }
412            
413            public String toString() {
414                    String l = "";
415                    for (String s : getWords()) {
416                            if (s == null) {
417                                    l += ",";
418                            } else {
419                                    l += s + ",";
420                            }
421                    }
422                    if (l.length() > 0) l = l.substring(0, l.length()-1);
423                    return getType() + "{" + l + "}";
424            }
425    
426    }