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