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 }