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.biojavax.ontology; 023 024import java.util.Collections; 025import java.util.Iterator; 026import java.util.Map; 027import java.util.NoSuchElementException; 028import java.util.Set; 029import java.util.TreeMap; 030import java.util.TreeSet; 031 032import org.biojava.ontology.AlreadyExistsException; 033import org.biojava.ontology.DefaultOps; 034import org.biojava.ontology.Ontology; 035import org.biojava.ontology.OntologyOps; 036import org.biojava.ontology.Term; 037import org.biojava.ontology.Triple; 038import org.biojava.ontology.Variable; 039import org.biojava.utils.AbstractChangeable; 040import org.biojava.utils.ChangeEvent; 041import org.biojava.utils.ChangeSupport; 042import org.biojava.utils.ChangeVetoException; 043 044/** 045 * Represents an ontology that can be compared to other ontologies. 046 * @author Richard Holland 047 * @author Mark Schreiber 048 * @since 1.5 049 */ 050public class SimpleComparableOntology extends AbstractChangeable implements ComparableOntology { 051 052 private String name; 053 private String description; 054 private Set terms = new TreeSet(); 055 private Map termsMap = new TreeMap(); 056 private Set triples = new TreeSet(); 057 private OntologyOps ops; 058 059 /** 060 * Creates a new instance of SimpleComparableOntology with the given, 061 * immutable, non-nullable name. 062 * @param name the name of the ontology. 063 */ 064 public SimpleComparableOntology(String name) { 065 if (name==null) throw new IllegalArgumentException("Name cannot be null"); 066 this.name = name; 067 this.description = null; 068 this.ops = new DefaultOps() { 069 public Set getRemoteTerms() { 070 return Collections.EMPTY_SET; 071 } 072 }; 073 } 074 075 // Hibernate requirement - not for public use. 076 protected SimpleComparableOntology() {} 077 078 /** 079 * {@inheritDoc} 080 * Ontologies are compared only by name. 081 */ 082 public int compareTo(Object o) { 083 if (o==this) return 0; 084 // Hibernate comparison - we haven't been populated yet 085 if (this.name==null) return -1; 086 // Normal comparison 087 Ontology them = (Ontology)o; 088 return this.name.compareTo(them.getName()); 089 } 090 091 /** 092 * {@inheritDoc} 093 * Ontologies are equal if their names are equal. 094 */ 095 public boolean equals(Object obj) { 096 if(this == obj) return true; 097 if (obj==null || !(obj instanceof Ontology)) return false; 098 // Hibernate comparison - we haven't been populated yet 099 if (this.name==null) return false; 100 // Normal comparison 101 Ontology them = (Ontology)obj; 102 return this.name.equals(them.getName()); 103 } 104 105 /** 106 * {@inheritDoc} 107 */ 108 public int hashCode() { 109 int hash = 17; 110 // Hibernate comparison - we haven't been populated yet 111 if (this.name==null) return hash; 112 // Normal comparison 113 return 31*hash + this.name.hashCode(); 114 } 115 116 /** 117 * {@inheritDoc} 118 * Form: "name" 119 */ 120 public String toString() { 121 return this.getName(); 122 } 123 124 /** 125 * {@inheritDoc} 126 */ 127 public boolean containsTerm(String name) { return this.termsMap.containsKey(name); } 128 129 /** 130 * {@inheritDoc} 131 */ 132 public Term getTerm(String s) throws NoSuchElementException { 133 if (!this.termsMap.containsKey(s)) throw new NoSuchElementException("Ontology does not have term with name "+s); 134 return (ComparableTerm)this.termsMap.get(s); 135 } 136 137 /** 138 * {@inheritDoc} 139 * If the term has to be created, it is added with the description "auto-generated by biojavax". 140 */ 141 public ComparableTerm getOrCreateTerm(String name) { 142 try { 143 if (!this.termsMap.containsKey(name)) return (ComparableTerm)this.createTerm(name,"auto-generated by biojavax",null); 144 else return (ComparableTerm)this.getTerm(name); 145 } catch (ChangeVetoException e) { 146 return null; 147 } catch (AlreadyExistsException e) { 148 return (ComparableTerm)this.getTerm(name); 149 } 150 } 151 152 153 /** 154 *{@inheritDoc} 155 */ 156 public ComparableTriple getOrCreateTriple(Term subject, Term object, Term predicate){ 157 158 try { 159 if (this.getTriples(subject, object, predicate).size() == 0) { 160 return (ComparableTriple)this.createTriple(subject, object, predicate, 161 predicate.getName()+"("+subject.getName()+", "+object.getName()+")", 162 null); 163 } 164 else return (ComparableTriple)this.getTriples(subject, object, predicate).iterator().next(); 165 } catch (ChangeVetoException e) { 166 return null; 167 } catch (AlreadyExistsException e) { 168 return (ComparableTriple)this.getTriples(subject, object, predicate).iterator().next(); 169 } 170 } 171 /** 172 * {@inheritDoc} 173 */ 174 public ComparableTerm getOrImportTerm(Term term) { 175 //if (term instanceof ComparableTerm) return (ComparableTerm)term; 176 try { 177 if (!this.termsMap.containsKey(term.getName())) return (ComparableTerm)this.importTerm(term,term.getName()); 178 else return (ComparableTerm)this.getTerm(term.getName()); 179 } catch (ChangeVetoException e) { 180 return null; 181 } 182 } 183 184 /** 185 * {@inheritDoc} 186 */ 187 public Term createTerm(String name, String description, Object[] synonyms) throws AlreadyExistsException, ChangeVetoException, IllegalArgumentException { 188 if (name==null) throw new IllegalArgumentException("Name cannot be null"); 189 if (this.termsMap.containsKey(name)) throw new AlreadyExistsException("Ontology already has term with this name"); 190 ComparableTerm ct = new SimpleComparableTerm(this,name,synonyms); 191 ct.setDescription(description); 192 if(!this.hasListeners(ComparableOntology.TERM)) { 193 this.termsMap.put(name,ct); 194 this.terms.add(ct); 195 } else { 196 ChangeEvent ce = new ChangeEvent( 197 this, 198 ComparableOntology.TERM, 199 ct, 200 this.termsMap.get(name) 201 ); 202 ChangeSupport cs = this.getChangeSupport(ComparableOntology.TERM); 203 synchronized(cs) { 204 cs.firePreChangeEvent(ce); 205 this.termsMap.put(name,ct); 206 this.terms.add(ct); 207 cs.firePostChangeEvent(ce); 208 } 209 } 210 return ct; 211 } 212 213 /** 214 * {@inheritDoc} 215 * This particular implementation merely copies the term into this ontology, 216 * and returns a pointer to the copied term. Thus the term 217 * becomes a part of this ontology instead of a pointer to another ontology. 218 * @see ComparableTerm 219 */ 220 public Term importTerm(Term t, String localName) throws ChangeVetoException, IllegalArgumentException { 221 if (localName==null) localName=t.getName(); 222 if (localName==null) throw new IllegalArgumentException("Name cannot be null"); 223 if (this.termsMap.containsKey(localName)) return (ComparableTerm)this.termsMap.get(localName); 224 ComparableTerm ct = new SimpleComparableTerm(this,localName,t.getSynonyms()); 225 ct.setDescription(t.getDescription()); 226 if(!this.hasListeners(ComparableOntology.TERM)) { 227 this.termsMap.put(localName,ct); 228 this.terms.add(ct); 229 } else { 230 ChangeEvent ce = new ChangeEvent( 231 this, 232 ComparableOntology.TERM, 233 ct, 234 this.termsMap.get(localName) 235 ); 236 ChangeSupport cs = this.getChangeSupport(ComparableOntology.TERM); 237 synchronized(cs) { 238 cs.firePreChangeEvent(ce); 239 this.termsMap.put(localName,ct); 240 this.terms.add(ct); 241 cs.firePostChangeEvent(ce); 242 } 243 } 244 return ct; 245 } 246 247 /** 248 * {@inheritDoc} 249 * If you call this method with plain Terms instead of ComparableTerms, it will 250 * import them into the local ontology first. This is done BEFORE the check to 251 * see if the triple already exists, as obviously it can't check until it has 252 * the right terms to check with. So you may find the terms get imported but the 253 * triple does not. Moral of the story: use ComparableTerm objects! 254 * @see ComparableTerm 255 */ 256 public Triple createTriple(Term subject, Term object, Term predicate, String name, String description) throws AlreadyExistsException, ChangeVetoException { 257 if (!(subject instanceof ComparableTerm)) subject = this.getOrImportTerm(subject); 258 if (!(object instanceof ComparableTerm)) object = this.getOrImportTerm(object); 259 if (!(predicate instanceof ComparableTerm)) predicate = this.getOrImportTerm(predicate); 260 if (this.containsTriple(subject,object,predicate)) 261 throw new AlreadyExistsException("Ontology already has triple with this subject/object/predicate combination"); 262 ComparableTriple ct = new SimpleComparableTriple(this,(ComparableTerm)subject,(ComparableTerm)object,(ComparableTerm)predicate); 263 if (!this.triples.contains(ct)) { 264 if(!this.hasListeners(ComparableOntology.TRIPLE)) { 265 this.triples.add(ct); 266 } else { 267 ChangeEvent ce = new ChangeEvent( 268 this, 269 ComparableOntology.TRIPLE, 270 ct, 271 null 272 ); 273 ChangeSupport cs = this.getChangeSupport(ComparableOntology.TRIPLE); 274 synchronized(cs) { 275 cs.firePreChangeEvent(ce); 276 this.triples.add(ct); 277 cs.firePostChangeEvent(ce); 278 } 279 } 280 } 281 return ct; 282 } 283 284 /** 285 * {@inheritDoc} 286 * If you call this method with a plain Term instead of a ComparableTerm, it may 287 * not match any of the terms in the ontology as they are all stored as 288 * ComparableTerms. So, use ComparableTerm objects! 289 * This method also removes all triples that the term is involved in. 290 * @see ComparableTerm 291 */ 292 public void deleteTerm(Term t) throws ChangeVetoException { 293 // Remove all Triples involving this term. 294 for (Iterator i = this.triples.iterator(); i.hasNext();) { 295 ComparableTriple ct = (ComparableTriple)i.next(); 296 if (ct.equals(t) || ct.getSubject().equals(t) || ct.getObject().equals(t) || ct.getPredicate().equals(t)) { 297 if(!this.hasListeners(ComparableOntology.TRIPLE)) { 298 i.remove(); 299 } else { 300 ChangeEvent ce = new ChangeEvent( 301 this, 302 ComparableOntology.TRIPLE, 303 null, 304 ct 305 ); 306 ChangeSupport cs = this.getChangeSupport(ComparableOntology.TRIPLE); 307 synchronized(cs) { 308 cs.firePreChangeEvent(ce); 309 i.remove(); 310 cs.firePostChangeEvent(ce); 311 } 312 } 313 } 314 } 315 // Remove the term. 316 if(!this.hasListeners(ComparableOntology.TERM)) { 317 if (t instanceof Triple) this.triples.remove(t); 318 else { 319 this.termsMap.remove(t.getName()); 320 this.terms.remove(t); 321 } 322 } else { 323 ChangeEvent ce = new ChangeEvent( 324 this, 325 ComparableOntology.TERM, 326 null, 327 t 328 ); 329 ChangeSupport cs = this.getChangeSupport(ComparableOntology.TERM); 330 synchronized(cs) { 331 cs.firePreChangeEvent(ce); 332 if (t instanceof Triple) this.triples.remove(t); 333 else { 334 this.termsMap.remove(t.getName()); 335 this.terms.remove(t); 336 } 337 cs.firePostChangeEvent(ce); 338 } 339 } 340 } 341 342 /** 343 * {@inheritDoc} 344 * If you call this method with plain Terms instead of ComparableTerms, it may 345 * not match any of the triples in the ontology as they are all stored as 346 * ComparableTerms. So, use ComparableTerm objects! The set returned is a set 347 * of ComparableTriple objects. 348 * @see ComparableTerm 349 * @see ComparableTriple 350 */ 351 public Set getTriples(Term subject, Term object, Term predicate) { 352 Set results = new TreeSet(); 353 for (Iterator i = this.triples.iterator(); i.hasNext();) { 354 ComparableTriple ct = (ComparableTriple)i.next(); 355 if ((subject==null || ct.getSubject().equals(subject)) && 356 (object==null || ct.getObject().equals(object)) && 357 (predicate==null || ct.getPredicate().equals(predicate))) results.add(ct); 358 } 359 return results; 360 } 361 362 /** 363 * {@inheritDoc} 364 * <b>Warning</b> this method gives access to the original 365 * Collection not a copy. This is required by Hibernate. If you 366 * modify the object directly the behaviour may be unpredictable. 367 */ 368 public void setTripleSet(Set triples) throws ChangeVetoException { this.triples = triples; } // must be original for Hibernate 369 370 /** 371 * {@inheritDoc} 372 * <b>Warning</b> this method gives access to the original 373 * Collection not a copy. This is required by Hibernate. If you 374 * modify the object directly the behaviour may be unpredictable. 375 */ 376 public Set getTripleSet() { return this.triples; } // must be original for Hibernate 377 378 /** 379 * {@inheritDoc} 380 * This will always return a set of ComparableTerm objects. It is not the original 381 * set so you are safe to modify it. 382 * @see ComparableTerm 383 */ 384 public Set getTerms() { return new TreeSet(this.termsMap.values()); } 385 386 /** 387 * {@inheritDoc} 388 * <b>Warning</b> this method gives access to the original 389 * Collection not a copy. This is required by Hibernate. If you 390 * modify the object directly the behaviour may be unpredictable. 391 * @see ComparableTerm 392 */ 393 public void setTermSet(Set terms) throws ChangeVetoException { 394 this.terms = terms; // must be original for Hibernate 395 this.termsMap.clear(); 396 for (Iterator i = this.terms.iterator(); i.hasNext(); ) { 397 ComparableTerm t = (ComparableTerm)i.next(); 398 this.termsMap.put(t.getName(),t); 399 } 400 } 401 402 /** 403 * {@inheritDoc} 404 * <b>Warning</b> this method gives access to the original 405 * Collection not a copy. This is required by Hibernate. If you 406 * modify the object directly the behaviour may be unpredictable. 407 */ 408 public Set getTermSet() { return this.terms; } // must be original for Hibernate 409 410 /** 411 * {@inheritDoc} 412 * If you call this method with plain Terms instead of ComparableTerms, it may 413 * not match any of the triples in the ontology as they are all stored as 414 * ComparableTerms. So, use ComparableTerm objects! The set returned is a set 415 * of ComparableTriple objects. 416 * @see ComparableTerm 417 * @see ComparableTriple 418 */ 419 public boolean containsTriple(Term subject, Term object, Term predicate) { 420 for (Iterator i = this.triples.iterator(); i.hasNext();) { 421 ComparableTriple ct = (ComparableTriple)i.next(); 422 if (ct.getSubject().equals(subject) && 423 ct.getObject().equals(object) && 424 ct.getPredicate().equals(predicate)) return true; 425 } 426 return false; 427 } 428 429 /** 430 * {@inheritDoc} 431 */ 432 public Term createTerm(String name) throws AlreadyExistsException, ChangeVetoException, IllegalArgumentException { 433 return this.createTerm(name,null,null); 434 } 435 436 /** 437 * {@inheritDoc} 438 */ 439 public Term createTerm(String name, String description) throws AlreadyExistsException, ChangeVetoException, IllegalArgumentException { 440 return this.createTerm(name,description,null); 441 } 442 443 /** 444 * {@inheritDoc} 445 * NOT IMPLEMENTED 446 */ 447 public Variable createVariable(String name, String description) throws AlreadyExistsException, ChangeVetoException, IllegalArgumentException { 448 throw new ChangeVetoException("BioSQL doesn't know what these are so we cowardly refuse to know too."); 449 } 450 451 /** 452 * {@inheritDoc} 453 */ 454 public String getDescription() { return this.description; } 455 456 /** 457 * {@inheritDoc} 458 */ 459 public void setDescription(String description) throws ChangeVetoException { 460 if(!this.hasListeners(ComparableOntology.DESCRIPTION)) { 461 this.description = description; 462 } else { 463 ChangeEvent ce = new ChangeEvent( 464 this, 465 ComparableOntology.DESCRIPTION, 466 description, 467 this.description 468 ); 469 ChangeSupport cs = this.getChangeSupport(ComparableOntology.DESCRIPTION); 470 synchronized(cs) { 471 cs.firePreChangeEvent(ce); 472 this.description = description; 473 cs.firePostChangeEvent(ce); 474 } 475 } 476 } 477 478 /** 479 * {@inheritDoc} 480 */ 481 public String getName() { return this.name; } 482 483 // Hibernate requirement - not for public use. 484 public void setName(String name) { this.name = name; } 485 486 /** 487 * {@inheritDoc} 488 */ 489 public OntologyOps getOps() { return this.ops; } 490 491 // Hibernate requirement - not for public use. 492 private Integer id; 493 494 /** 495 * Gets the Hibernate ID. Should be used with caution. 496 * @return the Hibernate ID, if using Hibernate. 497 */ 498 public Integer getId() { return this.id; } 499 500 /** 501 * Sets the Hibernate ID. Should be used with caution. 502 * @param id the Hibernate ID, if using Hibernate. 503 */ 504 public void setId(Integer id) { this.id = id;} 505 506} 507