001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021 022package org.biojava.nbio.ontology; 023 024import org.biojava.nbio.ontology.utils.AssertionFailure; 025 026import java.util.*; 027 028 029 030 031/** 032 * An ontology. 033 * 034 * <p>This is just a set of Term objects, and a set of 035 * Triple objects describing relationships between these terms. 036 * This class does not itself contain any reasoning functionality. Ontology is 037 * a collection of facts, or axioms.</p> 038 * 039 * @author Thomas Down 040 * @author Matthew Pocock 041 * 042 * @since 1.4 043 * @see org.biojavax.ontology.ComparableOntology 044 */ 045 046public interface Ontology { 047 048 049 /** 050 * Return the name of this ontology 051 * @return the name of the ontology 052 */ 053 054 public String getName(); 055 056 /** Set the name for this ontology 057 * 058 * @param name - the name 059 * 060 */ 061 public void setName(String name); 062 063 /** 064 * Return a human-readable description of this ontology, or the empty 065 * string if none is available 066 * @return the description of the term 067 */ 068 069 public String getDescription(); 070 071 /** set the description of this ontology 072 * 073 * @param description 074 */ 075 public void setDescription(String description); 076 077 078 079 /** 080 * Return all the terms in this ontology 081 * @return a Set of all Terms of the ontology. 082 */ 083 084 public Set<Term> getTerms(); 085 086 /** 087 * Fetch the term with the specified name. 088 * @param name the name of the term 089 * 090 * @return The term named <code>name</code> 091 * @throws NoSuchElementException if no term exists with that name 092 */ 093 094 public Term getTerm(String name) throws NoSuchElementException; 095 096 /** 097 * Return all triples from this ontology which match the supplied 098 * pattern. If any of the parameters of this method are <code>null</code>, 099 * they are treated as wildcards. 100 * 101 * @param subject The subject to search for, or <code>null</code> 102 * @param object The object to search for, or <code>null</code> 103 * @param predicate The relationship to search for, or <code>null</code>. 104 * @return a Set of triples 105 */ 106 107 public Set<Triple> getTriples(Term subject, Term object, Term predicate); 108 109 /** 110 * Return the associated OntologyOps. 111 * 112 * This method should be implemented by ontology 113 * implementors to allow OntoTools 114 * to get optimized access to some usefull ontology operations. It is not 115 * intended that users will ever invoke this. A sensible dumb implementation 116 * of this would return a per-ontology instance of DefaultOps. 117 * 118 * @return the OntologyOps instance associated with this instance. 119 */ 120 121 public OntologyOps getOps(); 122 123 /** 124 * Create a new term in this ontology. 125 * 126 * @param name The name of the term (must be unique)) 127 * @throws IllegalArgumentException if either <code>name</code> or 128 * <code>description</code> is <code>null</code>, or violates 129 * some other constraint of this implementation. 130 * @throws AlreadyExistsException if a term of this name already exists 131 * @return The newly created term. 132 * @throws ChangeVetoException 133 */ 134 135 public Term createTerm(String name) 136 throws 137 AlreadyExistsException, 138 139 IllegalArgumentException; 140 141 /** 142 * Create a new term in this ontology. 143 * 144 * @param name The name of the term (must be unique) 145 * @param description A human-readable description (may be empty) 146 * @throws IllegalArgumentException if either <code>name</code> or 147 * <code>description</code> is <code>null</code>, or violates 148 * some other constraint of this implementation. 149 * @throws AlreadyExistsException if a term of this name already exists 150 * @return The newly created term. 151 * @throws ChangeVetoException 152 */ 153 154 public Term createTerm(String name, String description) 155 throws 156 AlreadyExistsException, 157 158 IllegalArgumentException; 159 160 /** 161 * Create a new term in this ontology. 162 * 163 * @param name The name of the term (must be unique) 164 * @param description A human-readable description (may be empty) 165 * @param synonyms Some synonyms for this term. 166 * @throws IllegalArgumentException if either <code>name</code> or 167 * <code>description</code> is <code>null</code>, or violates 168 * some other constraint of this implementation. 169 * @throws AlreadyExistsException if a term of this name already exists 170 * @return The newly created term. 171 * @throws ChangeVetoException 172 */ 173 174 public Term createTerm(String name, String description, Object[] synonyms) 175 throws 176 AlreadyExistsException, 177 178 IllegalArgumentException; 179 180 /** 181 * Create a new term in this ontology that is used as a variable. 182 * 183 * @param name The name of the term (must be unique) 184 * @param description A human-readable description (may be empty) 185 * @throws IllegalArgumentException if either <code>name</code> or 186 * <code>description</code> is <code>null</code>, or violates 187 * some other constraint of this implementation. 188 * @throws AlreadyExistsException if a term of this name already exists 189 * @return The newly created term. 190 * @throws ChangeVetoException 191 */ 192 193 public Variable createVariable(String name, String description) 194 throws 195 AlreadyExistsException, 196 197 IllegalArgumentException; 198 199 /** 200 * Create a view of a term from another ontology. If the requested term 201 * has already been imported under that name, this method returns the existing 202 * RemoteTerm object. If the term that is being imported is itself a 203 * RemoteTerm instance then first unwrap the term back to the orriginal 204 * term it represents and then produce a RemoteTerm from that. If the term 205 * being imported orriginated from this ontology, then return that term 206 * unaltered. 207 * 208 * @param t the Term to import 209 * @param localName the local name to import it under, optionally null 210 * @return a Term 211 * @throws ChangeVetoException 212 * @throws IllegalArgumentException 213 */ 214 215 public Term importTerm(Term t, String localName) 216 throws 217 218 IllegalArgumentException; 219 220 /** 221 * Creates a new Triple. 222 * 223 * @param subject the subject Term 224 * @param object the object Term 225 * @param predicate the predicate Term 226 * @param name the name of the triple, or null 227 * @param description the description of the triple, or null 228 * @return a new Triple over these three terms 229 * @throws AlreadyExistsException if a triple already exists with the same 230 * subject, object and predicate, regardless of the name and description 231 * @throws ChangeVetoException 232 * @throws NullPointerException if subject, object or predicate are null 233 * @throws IllegalArgumentException if subject, object or predicate are not all 234 * from the same ontology 235 */ 236 public Triple createTriple(Term subject, Term object, Term predicate, String name, String description) 237 throws 238 AlreadyExistsException 239 ; 240 241 /** 242 * See if a triple exists in this ontology 243 * @param subject 244 * @param object 245 * @param predicate 246 * @return true if contained 247 */ 248 249 public boolean containsTriple(Term subject, Term object, Term predicate); 250 251 /** 252 * Remove a term from an ontology, together with all triples which refer to it. 253 * @param t 254 * @throws ChangeVetoException 255 */ 256 257 public void deleteTerm(Term t) ; 258 259 /** 260 * Determines if this ontology currently contains a term named <code>name</code> 261 * @param name 262 * @return true is contained 263 */ 264 265 public boolean containsTerm(String name); 266 267 /** 268 * A basic in-memory implementation of an ontology 269 * 270 * @author Thomas Down 271 * @author Matthew Pocock 272 * @since 1.3 273 */ 274 275 // AP: I am setting name and description to public changeable fields 276 // e.g during parsing of an .obo file we don't know them when the ontology is instanciated 277 public final class Impl 278 279 implements Ontology, java.io.Serializable { 280 /** 281 * 282 */ 283 private static final long serialVersionUID = -8064461497813727957L; 284 private final Map<String,Term> terms; 285 private final Set<Triple> triples; 286 private final Map<Term, Set<Triple>> subjectTriples; 287 private final Map<Term, Set<Triple>> objectTriples; 288 private final Map<Term, Set<Triple>> relationTriples; 289 private final Map<Term,RemoteTerm> remoteTerms; 290 private final Set<Term> localRemoteTerms; 291 292 private /*final*/ String name; 293 private /*final*/ String description; 294 private final OntologyOps ops; 295 296 { 297 terms = new HashMap<String, Term>(); 298 triples = new HashSet<Triple>(); 299 subjectTriples = new HashMap<Term, Set<Triple>>(); 300 objectTriples = new HashMap<Term, Set<Triple>>(); 301 relationTriples = new HashMap<Term, Set<Triple>>(); 302 remoteTerms = new HashMap<Term, RemoteTerm>(); 303 localRemoteTerms = new HashSet<Term>(); 304 } 305 306 public Impl(String name, String description) { 307 this.name = name; 308 this.description = description; 309 ops = new DefaultOps() { 310 /** 311 * 312 */ 313 private static final long serialVersionUID = -2135777733685713181L; 314 315 @Override 316 public Set<Term> getRemoteTerms() { 317 return localRemoteTerms; 318 } 319 }; 320 } 321 322 @Override 323 public String getName() { 324 return name; 325 } 326 327 @Override 328 public String getDescription() { 329 return description; 330 } 331 332 333 @Override 334 public void setDescription(String description){ 335 this.description = description; 336 } 337 338 @Override 339 public Set<Term> getTerms() { 340 return new HashSet<Term>(terms.values()); 341 } 342 343 @Override 344 public Term getTerm(String name) 345 throws NoSuchElementException 346 { 347 Term t = terms.get(name); 348 if (t == null) { 349 throw new NoSuchElementException("No term named '" + name + "'"); 350 } else { 351 return terms.get(name); 352 } 353 } 354 355 @Override 356 public Set<Triple> getTriples(Term subject, Term object, Term predicate) { 357 if(subject != null && subject.getOntology() != this) { 358 throw new IllegalArgumentException("Subject is not in this ontology: " + subject + " " + this); 359 } 360 361 if(object != null && object.getOntology() != this) { 362 throw new IllegalArgumentException("Object is not in this ontology: " + object + " " + this); 363 } 364 365 if(predicate != null && predicate.getOntology() != this) { 366 throw new IllegalArgumentException("Predicate is not in this ontology: " + predicate + " " + this); 367 } 368 369 if (subject != null) { 370 return filterTriples( subjectTriples.get(subject), null, object, predicate); 371 } else if (object != null) { 372 return filterTriples( objectTriples.get(object), subject, null, predicate); 373 } else if (predicate != null) { 374 return filterTriples( relationTriples.get(predicate), subject, object, null); 375 } else { 376 return filterTriples(triples, subject, object, predicate); 377 } 378 } 379 380 private Set<Triple> filterTriples(Set<Triple> base, Term subject, Term object, Term predicate) { 381 if (base == null) { 382 return Collections.EMPTY_SET; 383 } else if (subject == null && object == null && predicate == null) { 384 return Collections.unmodifiableSet(new HashSet<Triple>(base)); 385 } 386 387 Set<Triple> retval = new HashSet<Triple>(); 388 for (Iterator<Triple> i = base.iterator(); i.hasNext(); ) { 389 Triple t = i.next(); 390 if (subject != null && t.getSubject() != subject) { 391 continue; 392 } 393 if (object != null && t.getObject() != object) { 394 continue; 395 } 396 if (predicate != null && t.getPredicate() != predicate) { 397 continue; 398 } 399 retval.add(t); 400 } 401 return retval; 402 } 403 404 private void addTerm(Term t) 405 throws AlreadyExistsException, IllegalArgumentException 406 { 407 if (terms.containsKey(t.getName())) { 408 throw new AlreadyExistsException("Ontology " + getName() + " already contains " + t.toString()); 409 } 410 411 412 terms.put(t.getName(), t); 413 414 } 415 416 @Override 417 public Term createTerm(String name) 418 throws AlreadyExistsException, IllegalArgumentException 419 { 420 Term t = new Term.Impl(this, name); 421 addTerm(t); 422 return t; 423 } 424 425 @Override 426 public Term createTerm(String name, String description) 427 throws AlreadyExistsException, IllegalArgumentException 428 { 429 Term t = new Term.Impl(this, name, description); 430 addTerm(t); 431 return t; 432 } 433 434 @Override 435 public Term createTerm(String name, String description, Object[] synonyms) 436 throws AlreadyExistsException, IllegalArgumentException 437 { 438 Term t = new Term.Impl(this, name, description, synonyms); 439 addTerm(t); 440 return t; 441 } 442 443 @Override 444 public Variable createVariable(String name, String description) 445 throws 446 AlreadyExistsException, 447 448 IllegalArgumentException { 449 Variable var = new Variable.Impl(this, name, description); 450 addTerm(var); 451 return var; 452 } 453 454 public OntologyTerm createOntologyTerm(Ontology o) 455 throws AlreadyExistsException 456 { 457 OntologyTerm ot = new OntologyTerm.Impl(this, o); 458 addTerm(ot); 459 return ot; 460 } 461 462 463 @Override 464 public Term importTerm(Term t, String name) 465 throws IllegalArgumentException 466 { 467 // unpack any potential indirection - belt & braces 468 while(t instanceof RemoteTerm) { 469 t = ((RemoteTerm) t).getRemoteTerm(); 470 } 471 472 if(t.getOntology() == this) { 473 return t; 474 } 475 476 RemoteTerm rt = remoteTerms.get(t); 477 if (rt == null) { 478 rt = new RemoteTerm.Impl(this, t, name); 479 try { 480 addTerm(rt); 481 } catch (AlreadyExistsException e) { 482 throw new AssertionFailure("This term can not exist", e); 483 } 484 if(name == null) { 485 remoteTerms.put(t, rt); 486 } 487 localRemoteTerms.add(rt); 488 } 489 return rt; 490 } 491 492 @Override 493 public void deleteTerm(Term t) 494 495 { 496 String name = t.getName(); 497 if (terms.get(name) != t) { 498 return; // Should this be an exception? 499 } 500 501 terms.remove(name); 502 if(t instanceof Triple) { 503 removeTriple((Triple) t); 504 } 505 506 } 507 508 @Override 509 public boolean containsTerm(String name) { 510 return terms.containsKey(name); 511 } 512 513 private boolean containsTerm(Term t) { 514 return (terms.get(t.getName()) == t); 515 } 516 517 @Override 518 public boolean containsTriple(Term subject, Term object, Term predicate) { 519 if(!(subject.getOntology() == this)) return false; 520 if(!(object.getOntology() == this)) return false; 521 if(!(predicate.getOntology() == this)) return false; 522 523 return triples.contains(new Triple.Impl(subject, object, predicate)); 524 } 525 526 @Override 527 public Triple createTriple(Term subject, 528 Term object, 529 Term predicate, 530 String name, 531 String description) 532 throws 533 AlreadyExistsException, 534 IllegalArgumentException, 535 NullPointerException, 536 IllegalArgumentException 537 { 538 Triple t = new Triple.Impl(subject, object, predicate, name, description); 539 if (!containsTerm(subject)) { 540 throw new IllegalArgumentException("Ontology " + getName() + " doesn't contain " + subject); 541 } 542 if (!containsTerm(predicate)) { 543 throw new IllegalArgumentException("Ontology " + getName() + " doesn't contain " + predicate); 544 } 545 if (!containsTerm(object)) { 546 throw new IllegalArgumentException("Ontology " + getName() + " doesn't contain " + object); 547 } 548 if (triples.contains(t)) { 549 throw new AlreadyExistsException("Ontology " + getName() + " already contains " + t.toString()); 550 } 551 552 553 addTerm(t); 554 addTriple(t); 555 556 return t; 557 } 558 559 private void addTriple(Triple t) { 560 triples.add(t); 561 pushTriple(subjectTriples, t.getSubject(), t); 562 pushTriple(objectTriples, t.getObject(), t); 563 pushTriple(relationTriples, t.getPredicate(), t); 564 } 565 566 private void pushTriple(Map<Term,Set<Triple>> m, Term key, Triple t) { 567 Set<Triple> s = m.get(key); 568 if (s == null) { 569 s = new HashSet<Triple>(); 570 m.put(key, s); 571 } 572 s.add(t); 573 } 574 575 private void removeTriple(Triple t) { 576 triples.remove(t); 577 pullTriple(subjectTriples, t.getSubject(), t); 578 pullTriple(objectTriples, t.getObject(), t); 579 pullTriple(relationTriples, t.getPredicate(), t); 580 } 581 582 private void pullTriple(Map<Term, Set<Triple>> subjectTriples2, Term key, Triple t) { 583 Set<Triple> s = subjectTriples2.get(key); 584 if (s != null) { 585 s.remove(t); 586 } 587 } 588 589 @Override 590 public OntologyOps getOps() { 591 return ops; 592 } 593 594 @Override 595 public String toString() { 596 return "ontology: " + getName(); 597 } 598 599 @Override 600 public void setName(String name) { 601 this.name=name; 602 603 } 604 } 605} 606