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 }