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.bio.taxa; 023import java.util.Iterator; 024import java.util.Map; 025import java.util.Set; 026import java.util.TreeMap; 027import java.util.TreeSet; 028 029import org.biojava.utils.AbstractChangeable; 030import org.biojava.utils.ChangeEvent; 031import org.biojava.utils.ChangeSupport; 032import org.biojava.utils.ChangeVetoException; 033import org.biojavax.RichObjectFactory; 034 035/** 036 * Reference implementation of NCBITaxon. 037 * @author Richard Holland 038 * @author Mark Schreiber 039 * @author David Scott 040 * @since 1.5 041 */ 042public class SimpleNCBITaxon extends AbstractChangeable implements NCBITaxon { 043 044 protected Set names = new TreeSet(); 045 private Map namesMap = new TreeMap(); 046 private Integer parent; 047 private int NCBITaxID; 048 private String nodeRank; 049 private Integer geneticCode; 050 private Integer mitoGeneticCode; 051 private Integer leftValue; 052 private Integer rightValue; 053 private boolean isTaxonHidden = false; 054 055 private final static String TAXONISHIDDEN = "X"; 056 private final static int ROOTNCBIID = 1; 057 058 /** 059 * Creates a new instance of SimpleNCBITaxon based on the given taxon ID. 060 * @param NCBITaxID the underlying taxon ID from NCBI. 061 */ 062 public SimpleNCBITaxon(int NCBITaxID) { 063 this.NCBITaxID = NCBITaxID; 064 } 065 066 /** 067 * Creates a new instance of SimpleNCBITaxon based on the given taxon ID. 068 * It may not be null, else you'll get exceptions. 069 * @param NCBITaxID the underlying taxon ID from NCBI. 070 */ 071 public SimpleNCBITaxon(Integer NCBITaxID) { 072 this.NCBITaxID = NCBITaxID.intValue(); 073 } 074 075 // Hibernate requirement - not for public use. 076 protected SimpleNCBITaxon() {} 077 078 /** 079 * {@inheritDoc} 080 * NCBITaxon objects are compared only by their NCBITaxID fields. 081 */ 082 public int compareTo(Object o) { 083 if (o==this) return 0; 084 NCBITaxon them = (NCBITaxon)o; 085 return this.NCBITaxID-them.getNCBITaxID(); 086 } 087 088 /** 089 * {@inheritDoc} 090 * NCBITaxon objects are equal if their NCBITaxID fields match. 091 */ 092 public boolean equals(Object obj) { 093 if (this == obj) return true; 094 if (obj==null || !(obj instanceof NCBITaxon)) return false; 095 NCBITaxon them = (NCBITaxon)obj; 096 return this.NCBITaxID==them.getNCBITaxID(); 097 } 098 099 /** 100 * {@inheritDoc} 101 */ 102 public int hashCode() { 103 return this.NCBITaxID; 104 } 105 106 /** 107 * {@inheritDoc} 108 */ 109 public Set getNameClasses() { return this.namesMap.keySet(); } 110 111 /** 112 * {@inheritDoc} 113 */ 114 public Set getNames(String nameClass) throws IllegalArgumentException { 115 if (nameClass==null) throw new IllegalArgumentException("Name class cannot be null"); 116 Set items = (Set)this.namesMap.get(nameClass); 117 Set n = new TreeSet(); 118 if (items!=null) for (Iterator j = items.iterator(); j.hasNext(); ) { 119 SimpleNCBITaxonName name = (SimpleNCBITaxonName)j.next(); 120 n.add(name.getName()); 121 } 122 return n; 123 } 124 125 // Hibernate requirement - not for public use. 126 protected Set getNameSet() { return this.names; } // original for Hibernate 127 128 // Hibernate requirement - not for public use. 129 void setNameSet(Set names) { 130 this.names = names; // original for Hibernate 131 // convert set to map 132 this.namesMap.clear(); 133 for (Iterator i = names.iterator(); i.hasNext(); ) { 134 SimpleNCBITaxonName n = (SimpleNCBITaxonName)i.next(); 135 try { 136 this.addName(n.getNameClass(), n.getName()); 137 } catch (ChangeVetoException e) { 138 throw new RuntimeException("Database contents don't add up",e); 139 } 140 } 141 } 142 143 /** 144 * {@inheritDoc} 145 */ 146 public void addName(String nameClass, String name) throws IllegalArgumentException,ChangeVetoException { 147 if (name==null) throw new IllegalArgumentException("Name cannot be null"); 148 if (nameClass==null) throw new IllegalArgumentException("Name class cannot be null"); 149 SimpleNCBITaxonName n = new SimpleNCBITaxonName(nameClass, name); 150 if(!this.hasListeners(NCBITaxon.NAMES)) { 151 if (!this.namesMap.containsKey(nameClass)) this.namesMap.put(nameClass,new TreeSet()); 152 ((Set)this.namesMap.get(nameClass)).add(n); 153 this.names.add(n); 154 } else { 155 ChangeEvent ce = new ChangeEvent( 156 this, 157 NCBITaxon.NAMES, 158 name, 159 ((Set)this.namesMap.get(nameClass)).contains(n)?name:null 160 ); 161 ChangeSupport cs = this.getChangeSupport(NCBITaxon.NAMES); 162 synchronized(cs) { 163 cs.firePreChangeEvent(ce); 164 if (!this.namesMap.containsKey(nameClass)) this.namesMap.put(nameClass,new TreeSet()); 165 ((Set)this.namesMap.get(nameClass)).add(n); 166 this.names.add(n); 167 cs.firePostChangeEvent(ce); 168 } 169 } 170 } 171 172 /** 173 * {@inheritDoc} 174 */ 175 public boolean removeName(String nameClass, String name) throws IllegalArgumentException,ChangeVetoException { 176 if (name==null) throw new IllegalArgumentException("Name cannot be null"); 177 if (nameClass==null) throw new IllegalArgumentException("Name class cannot be null"); 178 SimpleNCBITaxonName n = new SimpleNCBITaxonName(nameClass, name); 179 if (!this.namesMap.containsKey(nameClass)) return false; 180 boolean results; 181 if(!this.hasListeners(NCBITaxon.NAMES)) { 182 results = ((Set)this.namesMap.get(nameClass)).remove(n); 183 this.names.remove(n); 184 } else { 185 ChangeEvent ce = new ChangeEvent( 186 this, 187 NCBITaxon.NAMES, 188 null, 189 name 190 ); 191 ChangeSupport cs = this.getChangeSupport(NCBITaxon.NAMES); 192 synchronized(cs) { 193 cs.firePreChangeEvent(ce); 194 results = ((Set)this.namesMap.get(nameClass)).remove(n); 195 this.names.remove(n); 196 cs.firePostChangeEvent(ce); 197 } 198 } 199 return results; 200 } 201 202 /** 203 * {@inheritDoc} 204 */ 205 public boolean containsName(String nameClass, String name) throws IllegalArgumentException { 206 if (name==null) throw new IllegalArgumentException("Name cannot be null"); 207 if (nameClass==null) throw new IllegalArgumentException("Name class cannot be null"); 208 if (!this.namesMap.containsKey(nameClass)) return false; 209 SimpleNCBITaxonName n = new SimpleNCBITaxonName(nameClass, name); 210 return ((Set)this.namesMap.get(nameClass)).contains(n); 211 } 212 213 protected final Map getNamesMap() { 214 return namesMap; 215 } 216 217 /** 218 * {@inheritDoc} 219 */ 220 public Integer getParentNCBITaxID() { return this.parent; } 221 222 /** 223 * {@inheritDoc} 224 */ 225 public void setParentNCBITaxID(Integer parent) throws ChangeVetoException { 226 if(!this.hasListeners(NCBITaxon.PARENT)) { 227 this.parent = parent; 228 } else { 229 ChangeEvent ce = new ChangeEvent( 230 this, 231 NCBITaxon.PARENT, 232 parent, 233 this.parent 234 ); 235 ChangeSupport cs = this.getChangeSupport(NCBITaxon.PARENT); 236 synchronized(cs) { 237 cs.firePreChangeEvent(ce); 238 this.parent = parent; 239 cs.firePostChangeEvent(ce); 240 } 241 } 242 } 243 244 /** 245 * {@inheritDoc} 246 */ 247 public int getNCBITaxID() { return this.NCBITaxID; } 248 249 // Hibernate requirement - not for public use. 250 void setNCBITaxID(int NCBITaxID) { this.NCBITaxID = NCBITaxID; } 251 252 /** 253 * {@inheritDoc} 254 */ 255 public String getNodeRank() { return this.nodeRank; } 256 257 /** 258 * Setter for property nodeRank. 259 * @param nodeRank New value of property nodeRank. 260 * @throws org.biojava.utils.ChangeVetoException in case of objections. 261 */ 262 public void setNodeRank(String nodeRank) throws ChangeVetoException { 263 if(!this.hasListeners(NCBITaxon.NODERANK)) { 264 this.nodeRank = nodeRank; 265 } else { 266 ChangeEvent ce = new ChangeEvent( 267 this, 268 NCBITaxon.NODERANK, 269 nodeRank, 270 this.nodeRank 271 ); 272 ChangeSupport cs = this.getChangeSupport(NCBITaxon.NODERANK); 273 synchronized(cs) { 274 cs.firePreChangeEvent(ce); 275 this.nodeRank = nodeRank; 276 cs.firePostChangeEvent(ce); 277 } 278 } 279 } 280 281 /** 282 * {@inheritDoc} 283 */ 284 public Integer getGeneticCode() { return this.geneticCode; } 285 286 /** 287 * {@inheritDoc} 288 */ 289 public void setGeneticCode(Integer geneticCode) throws ChangeVetoException { 290 if(!this.hasListeners(NCBITaxon.GENETICCODE)) { 291 this.geneticCode = geneticCode; 292 } else { 293 ChangeEvent ce = new ChangeEvent( 294 this, 295 NCBITaxon.GENETICCODE, 296 nodeRank, 297 this.nodeRank 298 ); 299 ChangeSupport cs = this.getChangeSupport(NCBITaxon.GENETICCODE); 300 synchronized(cs) { 301 cs.firePreChangeEvent(ce); 302 this.geneticCode = geneticCode; 303 cs.firePostChangeEvent(ce); 304 } 305 } 306 } 307 308 /** 309 * Getter for property mitoGeneticCode. Returns Persistent.NULL_INTEGER if null. 310 * @return Value of property mitoGeneticCode. 311 */ 312 public Integer getMitoGeneticCode() { return this.mitoGeneticCode; } 313 314 /** 315 * {@inheritDoc} 316 */ 317 public void setMitoGeneticCode(Integer mitoGeneticCode) throws ChangeVetoException { 318 if(!this.hasListeners(NCBITaxon.MITOGENETICCODE)) { 319 this.mitoGeneticCode = mitoGeneticCode; 320 } else { 321 ChangeEvent ce = new ChangeEvent( 322 this, 323 NCBITaxon.MITOGENETICCODE, 324 mitoGeneticCode, 325 this.mitoGeneticCode 326 ); 327 ChangeSupport cs = this.getChangeSupport(NCBITaxon.MITOGENETICCODE); 328 synchronized(cs) { 329 cs.firePreChangeEvent(ce); 330 this.mitoGeneticCode = mitoGeneticCode; 331 cs.firePostChangeEvent(ce); 332 } 333 } 334 } 335 336 /** 337 * {@inheritDoc} 338 */ 339 public Integer getLeftValue() { return this.leftValue; } 340 341 /** 342 * {@inheritDoc} 343 */ 344 public void setLeftValue(Integer leftValue) throws ChangeVetoException { 345 if(!this.hasListeners(NCBITaxon.LEFTVALUE)) { 346 this.leftValue = leftValue; 347 } else { 348 ChangeEvent ce = new ChangeEvent( 349 this, 350 NCBITaxon.LEFTVALUE, 351 leftValue, 352 this.leftValue 353 ); 354 ChangeSupport cs = this.getChangeSupport(NCBITaxon.LEFTVALUE); 355 synchronized(cs) { 356 cs.firePreChangeEvent(ce); 357 this.leftValue = leftValue; 358 cs.firePostChangeEvent(ce); 359 } 360 } 361 } 362 363 /** 364 * {@inheritDoc} 365 */ 366 public Integer getRightValue() { return this.rightValue; } 367 368 /** 369 * {@inheritDoc} 370 */ 371 public void setRightValue(Integer rightValue) throws ChangeVetoException { 372 if(!this.hasListeners(NCBITaxon.RIGHTVALUE)) { 373 this.rightValue = rightValue; 374 } else { 375 ChangeEvent ce = new ChangeEvent( 376 this, 377 NCBITaxon.RIGHTVALUE, 378 rightValue, 379 this.rightValue 380 ); 381 ChangeSupport cs = this.getChangeSupport(NCBITaxon.RIGHTVALUE); 382 synchronized(cs) { 383 cs.firePreChangeEvent(ce); 384 this.rightValue = rightValue; 385 cs.firePostChangeEvent(ce); 386 } 387 } 388 } 389 390 /** 391 * Returns the name of this taxon entry in the form: 392 * scientific (common) 393 * or if there is no common name: 394 * scientific 395 * or if there are no scientific names at all, the empty string. 396 * @return the display name as described above. 397 */ 398 public String getDisplayName() { 399 StringBuffer sb = new StringBuffer(); 400 Set sciNames = this.getNames(NCBITaxon.SCIENTIFIC); 401 Set comNames = this.getNames(NCBITaxon.COMMON); 402 if (sciNames.size()>0) { 403 sb.append((String)sciNames.iterator().next()); 404 if (comNames.size()>0) { 405 sb.append(" ("); 406 sb.append((String)comNames.iterator().next()); 407 sb.append(")"); 408 } 409 } 410 return sb.toString(); 411 } 412 413 /** 414 * Returns the taxonomy hierarchy of this taxon entry in the form: 415 * most specific; less specific; ...; least specific. 416 * It follows the chain up the tree as far as it can, and will stop 417 * at the first one it comes to that returns null for getParentNCBITaxID(). 418 * If this taxon entry has no scientific name, you will get the string ".". 419 * @return the display name as described above. 420 */ 421 /* 422 * (non-Javadoc) 423 * @see com.bioperception.bio.taxa.NCBITaxon2#isTaxonHidden() 424 */ 425 public final boolean isTaxonHidden() { 426 return isTaxonHidden; 427 } 428 429 /* 430 * (non-Javadoc) 431 * @see com.bioperception.bio.taxa.NCBITaxon2#setTaxonHidden(boolean) 432 */ 433 public final void setTaxonHidden(final boolean isTaxonHidden) throws ChangeVetoException { 434 if(!this.hasListeners(HIDDEN)) { 435 this.isTaxonHidden = isTaxonHidden; 436 } else { 437 final ChangeEvent ce = new ChangeEvent( this, HIDDEN, new Boolean(isTaxonHidden), new Boolean(this.isTaxonHidden)); 438 final ChangeSupport cs = this.getChangeSupport(HIDDEN); 439 synchronized(cs) { 440 cs.firePreChangeEvent(ce); 441 this.isTaxonHidden = isTaxonHidden; 442 cs.firePostChangeEvent(ce); 443 } 444 } 445 } 446 447 // Hibernate requirement - not for public use. 448 String getTaxonHiddenChar() { 449 return isTaxonHidden()?TAXONISHIDDEN:null; 450 } 451 452 // Hibernate requirement - not for public use. 453 void setTaxonHiddenChar(final String isHiddenChar) throws ChangeVetoException { 454 setTaxonHidden(isHiddenChar!=null || (isHiddenChar!=null && isHiddenChar.length() > 0));// any character will set 455 } 456 457 /** 458 * Returns the taxonomy hierarchy of this taxon entry in the form: 459 * most specific; less specific; ...; least specific. 460 * It follows the chain up the tree as far as it can, and will stop 461 * at the first one it comes to that returns null for getParentNCBITaxID(). 462 * If this taxon entry has no scientific name, you will get the string ".". 463 * @return the display name as described above. 464 */ 465 public String getNameHierarchy() { 466 StringBuffer sb = new StringBuffer(); 467 boolean first = true; 468 Integer parent = this.getParentNCBITaxID(); 469 while (parent!=null) { 470 NCBITaxon t = (NCBITaxon)RichObjectFactory.getObject(SimpleNCBITaxon.class, new Object[]{parent}); 471 Set sciNames = t.getNames(NCBITaxon.SCIENTIFIC); 472// System.out.println("SimpleNCBITaxon2.getNameHierarchy-t:"+t+", t.isTaxonHidden? "+t.isTaxonHidden()+", isRoot? "+isRoot(t)+", sciNames:"+sciNames); 473 if (sciNames.size()>0) { 474 if (!t.isTaxonHidden() && !isRoot(t)) {// root is NOT hidden - but also not displayed 475 if (!first) sb.insert(0,"; "); 476 else first = false; 477 sb.insert(0,(String)sciNames.iterator().next()); 478 } 479 // Don't get into endless loop if child's parent is itself. 480 if (t.getParentNCBITaxID().equals(new Integer(t.getNCBITaxID()))) parent = null; 481 else parent = t.getParentNCBITaxID(); 482 } 483 // Also don't go up past a parent that doesn't have a name. 484 else parent = null; 485 } 486 sb.append("."); 487 return sb.toString(); 488 } 489 490 491 private final static boolean isRoot(final NCBITaxon theTaxon) { 492 return isRoot(theTaxon.getNCBITaxID()); 493 } 494 495 private final static boolean isRoot(final int theNCBIId) { 496 return theNCBIId==ROOTNCBIID; 497 } 498 499 /** 500 * {@inheritDoc} 501 * Form: "taxid:[name,name...]" 502 */ 503 public String toString() { 504 StringBuffer sb = new StringBuffer(); 505 sb.append("taxid:"); 506 sb.append(this.NCBITaxID); 507 sb.append("["); 508 for (Iterator i = this.getNameSet().iterator(); i.hasNext(); ) { 509 SimpleNCBITaxonName n = (SimpleNCBITaxonName)i.next(); 510 sb.append(n); 511 if (i.hasNext()) sb.append(","); 512 } 513 sb.append("]"); 514 return sb.toString(); 515 } 516 517 // Hibernate requirement - not for public use. 518 private Integer id; 519 520 /** 521 * Gets the Hibernate ID. Should be used with caution. 522 * @return the Hibernate ID, if using Hibernate. 523 */ 524 public Integer getId() { return this.id; } 525 526 /** 527 * Sets the Hibernate ID. Should be used with caution. 528 * @param id the Hibernate ID, if using Hibernate. 529 */ 530 public void setId(Integer id) { this.id = id;} 531 532} 533