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