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 }