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.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 statements.
034     * 
035     * @author Tobias Kuhn
036     */
037    public abstract class OntologyElement implements Comparable<OntologyElement> {
038            
039            private Ontology ontology;
040            
041            private final Vector<Statement> statements = new Vector<Statement>();
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                            Statement statement = Statement.loadStatement(lines[i], oe);
085                            oe.statements.add(statement);
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 statements.
226             * 
227             * @return The statements.
228             */
229            public List<Statement> getStatements() {
230                    return new ArrayList<Statement>(statements);
231            }
232            
233            /**
234             * Returns the ACE sentences of the article text.
235             * 
236             * @return The ACE sentences.
237             */
238            public List<Sentence> getSentences() {
239                    ArrayList<Sentence> sentences = new ArrayList<Sentence>();
240                    for (Statement s : statements) {
241                            if (s instanceof Sentence) {
242                                    sentences.add((Sentence) s);
243                            }
244                    }
245                    return sentences;
246            }
247            
248            /**
249             * Edits a statement of the article. The old statement is replaced by the new statement.
250             * 
251             * @param oldStatement The statement that should be edited.
252             * @param newStatement The new statement.
253             * @return An integer value denoting the success/failure of the operation.
254             * @see Ontology#commitSentence(Sentence)
255             */
256            public int edit(Statement oldStatement, Statement newStatement) {
257                    List<Statement> newStatements = new ArrayList<Statement>();
258                    newStatements.add(newStatement);
259                    int success = edit(oldStatement, newStatements);
260                    return success;
261            }
262            
263            /**
264             * Edits a statement of the article. The old statement is replaced by the new statements.
265             * 
266             * @param oldStatement The statement that should be edited.
267             * @param newStatements The new statements.
268             * @return An integer value denoting the success/failure of the operation.
269             * @see Ontology#commitSentence(Sentence)
270             */
271            public int edit(Statement oldStatement, List<Statement> newStatements) {
272                    log("edit statement of " + getWord() + ": " + oldStatement.getText() +
273                                    " > " + getStatementsString(newStatements));
274                    
275                    synchronized (ontology) {
276                            if (statements.contains(oldStatement)) {
277                                    int i = statements.indexOf(oldStatement);
278                                    statements.remove(i);
279                                    statements.addAll(i, newStatements);
280                            } else {
281                                    log("error: statement is not around anymore");
282                                    statements.addAll(0, newStatements);
283                            }
284                            int success = 0;
285                            if (ontology != null) {
286                                    if (oldStatement instanceof Sentence) {
287                                            ontology.retractSentence((Sentence) oldStatement);
288                                    }
289                                    for (Statement s : newStatements) {
290                                            if (s instanceof Sentence) {
291                                                    int successThis = ontology.commitSentence((Sentence) s);
292                                                    if (successThis > success) success = successThis;
293                                            }
294                                    }
295                                    ontology.save(this);
296                            }
297                            return success;
298                    }
299            }
300            
301            /**
302             * Adds one new statement to the article. One has to specify in front of which
303             * statement the new statement should be added.
304             * 
305             * @param followingStatement The statement in front of which the new statement should be added,
306             *   or null if the statement should be added to the end of the article.
307             * @param newStatement The new statement to be added.
308             * @return An integer value denoting the success/failure of the operation.
309             * @see Ontology#commitSentence(Sentence)
310             */
311            public int add(Statement followingStatement, Statement newStatement) {
312                    List<Statement> newStatements = new ArrayList<Statement>();
313                    newStatements.add(newStatement);
314                    int success = add(followingStatement, newStatements);
315                    return success;
316            }
317            
318            /**
319             * Adds one or more new statements to the article. It has to be specified in front of which
320             * statement the new statement should be added.
321             * 
322             * @param followingStatement The statement in front of which the new statements should be added,
323             *   or null if the statements should be added to the end of the article.
324             * @param newStatements The new statements to be added.
325             * @return An integer value denoting the success/failure of the operation.
326             * @see Ontology#commitSentence(Sentence)
327             */
328            public int add(Statement followingStatement, List<Statement> newStatements) {
329                    log("add statements of " + getWord() + ": " + getStatementsString(newStatements));
330    
331                    synchronized (ontology) {
332                            if (statements.contains(followingStatement)) {
333                                    statements.addAll(statements.indexOf(followingStatement), newStatements);
334                            } else {
335                                    if (followingStatement != null) {
336                                            log("error: statement is not around anymore");
337                                    }
338                                    statements.addAll(newStatements);
339                            }
340                            int success = 0;
341                            if (ontology != null) {
342                                    for (Statement s : newStatements) {
343                                            if (s instanceof Sentence) {
344                                                    int successThis = ontology.commitSentence((Sentence) s);
345                                                    if (successThis > success) success = successThis;
346                                            }
347                                    }
348                                    ontology.save(this);
349                            }
350                            return success;
351                    }
352            }
353            
354            private String getStatementsString(List<Statement> statements) {
355                    String result = "";
356                    for (Statement s : statements) {
357                            result += s.getText() + " ";
358                    }
359                    return result;
360            }
361            
362            /**
363             * Removes the given statement from the article.
364             * 
365             * @param statement The statement to be removed.
366             */
367            public void remove(Statement statement) {
368                    synchronized (ontology) {
369                            if (statements.contains(statement)) {
370                                    log("remove statement: " + statement.getText());
371                                    statements.remove(statement);
372                            }
373                            if (ontology != null) {
374                                    if (statement instanceof Sentence) {
375                                            ontology.retractSentence((Sentence) statement);
376                                    }
377                                    ontology.save(this);
378                            }
379                    }
380            }
381            
382            /**
383             * Returns the lexicon entries (one for each word form).
384             * 
385             * @return The lexicon entries.
386             */
387            List<LexiconEntry> getLexiconEntries() {
388                    return null;
389            }
390            
391            /**
392             * Returns the URI of the ontology element. This URI is a concatenation of the
393             * ontology URI and the URI suffix of the ontology element.
394             * 
395             * @return The URI.
396             * @see #getURISuffix()
397             */
398            public final URI getURI() {
399                    String ontologyURI = "";
400                    if (ontology != null) {
401                            ontologyURI = ontology.getURI();
402                    }
403                    
404                    URI uri = null;
405                    try {
406                            uri = new URI(ontologyURI + getURISuffix());
407                    } catch (URISyntaxException ex) {
408                            ex.printStackTrace();
409                    }
410                    return uri;
411            }
412            
413            /**
414             * Returns the URI suffix of this ontology element. For example "#country".
415             * 
416             * @return The URI suffix.
417             * @see #getURI()
418             */
419            public String getURISuffix() {
420                    return "#" + getWord();
421            }
422            
423            final void setId(long id) {
424                    this.id = id;
425            }
426            
427            final long getId() {
428                    return id;
429            }
430            
431            /**
432             * Writes the text to the log file.
433             * 
434             * @param text The text to be written to the log file.
435             */
436            protected void log(String text) {
437                    if (ontology != null) {
438                            ontology.log(text);
439                    }
440            }
441            
442            /**
443             * Serializes this ontology element as a string.
444             * 
445             * @return The serialized ontology element.
446             */
447            String serialize() {
448                    String s = "type:";
449                    s += getInternalType() + "\nwords:";
450                    for (String word : getWords()) {
451                            if (word == null) {
452                                    s += ";";
453                            } else {
454                                    s += word + ";";
455                            }
456                    }
457                    s += "\n";
458                    for (Statement statement : statements) {
459                            s += statement.serialize();
460                    }
461                    return s;
462            }
463    
464            public int compareTo(OntologyElement e) {
465                    return getHeadword().toLowerCase().compareTo(e.getHeadword().toLowerCase());
466            }
467            
468            public String toString() {
469                    String l = "";
470                    for (String s : getWords()) {
471                            if (s == null) {
472                                    l += ",";
473                            } else {
474                                    l += s + ",";
475                            }
476                    }
477                    if (l.length() > 0) l = l.substring(0, l.length()-1);
478                    return getType() + "{" + l + "}";
479            }
480    
481    }