001 /* 002 * CRIMSON 003 * Copyright (c) 2006, Stephen Fisher, Susan Davidson, and Junhyong Kim, 004 * University of Pennsylvania. 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License as 008 * published by the Free Software Foundation; either version 2 of the 009 * License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, but 012 * WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 * 02110-1301 USA. 020 * 021 * @(#)Species.java 022 */ 023 024 package edu.upenn.crimson; 025 026 import java.util.HashSet; 027 import java.util.Iterator; 028 029 /** 030 * A single species object. 031 * 032 * Species can only exist within a tree and thus the species is added 033 * to a tree when it is created. Because species must exist within 034 * trees, copy(Tree) is used instead of clone() when duplicating 035 * a species for inclusion in a subtree. 036 * 037 * @author Stephen Fisher 038 * @version $Id: Species.java,v 1.33 2007/05/16 18:55:58 fisher Exp $ 039 */ 040 041 public class Species implements Comparable { 042 /** 043 * The ID of the species. This must be unique for each 044 * Species and is case insensitive. 045 */ 046 private String id = ""; 047 048 /** 049 * Level from root to this species. Can't rely on level for 050 * determining if the species is a root because it's not 051 * guarenteed that level values will be computed for a tree. 052 */ 053 private int level = 0; 054 055 /** Length from parent to this species (default = 1.0). */ 056 private double stemLength = 0.0; 057 058 /** Length from root to this species. */ 059 private double tempDepth = 0.0; 060 061 /** The parent will be null if this Species is the root. */ 062 private Species parent = null; 063 064 /** This contains the set of children. */ 065 private Species[] children = null; 066 067 /** 068 * This is used by nextChild() to allow for traversing the tree in 069 * a recursive like fashion but without using recursion. 070 * Recursion with very large trees is prone to memory issues. 071 */ 072 private int childIndex = -1; 073 074 public Species(int id, Tree tree) { 075 this(String.valueOf(id), tree); 076 } 077 078 public Species(String id, Tree tree) { 079 this(id, 0.0, 0, tree); 080 } 081 082 public Species(String id, double stemLength, int level, Tree tree) { 083 this.id = id.trim().toUpperCase(); 084 this.stemLength = stemLength; 085 this.level = level; 086 087 // by protecting against null trees, we are effectively 088 // allowing for the creation of species that are not attached 089 // to trees. These will need to be attached to a Tree 090 // manually. 091 if (tree != null) tree.addSpecies(this); 092 } 093 094 //-------------------------------------------------------------------------- 095 // Setters and Getters 096 097 /** The species ID is forced to be upper case. */ 098 public void setID(String id) { this.id = id.trim().toUpperCase(); } 099 100 public String getID() { return id; } 101 102 public void setStemLength(double stemLength) { this.stemLength = stemLength; } 103 104 public double getStemLength() { return stemLength; } 105 106 public void setTempDepth(double tempDepth) { this.tempDepth = tempDepth; } 107 108 public double getTempDepth() { return tempDepth; } 109 110 public void setLevel(int level) { this.level = level; } 111 112 public int getLevel() { return level; } 113 114 public void setParent(Species parent) { this.parent = parent; } 115 116 public Species getParent() { return parent; } 117 118 public Species[] getChildren() { return children; } 119 120 //-------------------------------------------------------------------------- 121 // Miscellaneous Methods 122 123 /** 124 * This will compute the length to the root, assuming the parent's 125 * root length has already been computed. 126 */ 127 public void computeTempDepth() { 128 // if null parent then we're root and have no root length 129 if (parent == null) { this.tempDepth = 0; } 130 else { this.tempDepth = parent.getTempDepth() + stemLength; } 131 } 132 133 /** 134 * This will compute the level of the species, assuming the 135 * parent's level has already been computed. 136 */ 137 public void computeLevel() { 138 // if null parent then we're root and have a level of 0 139 if (parent == null) { this.level = 0; } 140 else { this.level = parent.getLevel() + 1; } 141 } 142 143 /** 144 * This will set the temporal depth and level at the same time. 145 * If both values are needed, this is more efficient than setting 146 * each value separately as it will only require propogating 147 * through the tree once. 148 */ 149 public void setTempDepth_Level(double parentLength, int level) { 150 this.tempDepth = parentLength + stemLength; 151 this.level = level; 152 153 if (children != null) { 154 for (int i = 0; i < children.length; i++) { 155 children[i].setTempDepth_Level(this.tempDepth, level + 1); 156 } 157 } 158 } 159 160 /** 161 * This will add the child to the set of children and then set the 162 * parent of the child to be this Species object. 163 */ 164 public void addChild(Species child) { 165 if (numChildren() == 0) { 166 children = new Species[1]; 167 } else { 168 Species[] tmp = new Species[children.length + 1]; 169 System.arraycopy(children, 0, tmp, 0, children.length); 170 children = tmp; 171 } 172 children[children.length - 1] = child; 173 child.parent = this; 174 } 175 176 /** 177 * This will remove the child from the set of children. Children 178 * can only be removed by the Tree, so that the Tree can 179 * track the species in the tree. 180 */ 181 public void removeChild(Species child) { 182 if (children == null) { 183 return; 184 } else if (children.length == 1) { 185 children = null; 186 } else { 187 Species[] tmp = new Species[children.length - 1]; 188 int j = 0; 189 for (int i = 0; i < children.length; i++) { 190 if (children[i] != child) tmp[j++] = children[i]; 191 } 192 children = tmp; 193 } 194 195 child.parent = null; 196 } 197 198 /** Returns the number of children. */ 199 public int numChildren() { 200 if (children == null) return 0; 201 else return children.length; 202 } 203 204 /** 205 * It is assumed that all trees have a single root. Thus the root 206 * will not have any siblings. If a species doesn't have a parent, 207 * then it is the root and will return an empty HashSet. 208 * Otherwise, the siblings are computed from the parent's 209 * children. 210 */ 211 public HashSet getSiblings() { 212 HashSet siblings = new HashSet(); 213 214 // if no parent, then this is a root 215 if (parent == null) return siblings; 216 217 // we know the parent has at least one child (us) 218 for (int i = 0; i < parent.children.length; i++) { 219 Species sibling = parent.children[i]; 220 if (sibling != this) siblings.add(sibling); 221 } 222 223 return siblings; 224 } 225 226 /** 227 * Returns true if this species is a leaf (ie has no children). 228 */ 229 public boolean isLeaf() { 230 if (children == null) return true; 231 else return false; 232 } 233 234 /** 235 * Returns true if this species doesn't have a parent. Can't rely 236 * on level for determining if the species is a root because it's 237 * not guarenteed that level values will be computed for a tree. 238 */ 239 public boolean isRoot() { 240 if (parent == null) return true; 241 else return false; 242 } 243 244 /** Returns true if this node is binary. */ 245 public boolean isBinary() { 246 if (isLeaf() || numChildren() == 2) { 247 return true; 248 } else { 249 // we have either 1 or more than 2 children, so not binary 250 return false; 251 } 252 } 253 254 /** We use the hash of the ID as the object's hash code. */ 255 public int hashCode() { return id.hashCode(); } 256 257 /** When comparing Species to be equal, we compare their IDs. */ 258 public boolean equals(Object obj) { 259 if ((obj != null) && (((Species) obj).getID() == id)) return true; 260 return false; 261 } 262 263 /** 264 * When comparing Species objects, we actually just compare the 265 * index values. 266 */ 267 public int compareTo(Object obj) { return id.compareTo(((Species) obj).id); } 268 269 /** 270 * This will return a copy of the current species WITHOUT the 271 * parent, children, or level. This is meant to be used when 272 * coping a species from a tree to be included in a subtree. In 273 * this case, the level, parent, and children values are not 274 * meaningful. The current species is stored in origSpecies, so 275 * that tree sampling routines can connect this subtree to the 276 * original tree. 277 */ 278 public Species copy(Tree tree) { return new Species(id, stemLength, 0, tree); } 279 280 public String toStringFull() { 281 String out = ""; 282 283 out += "ID: " + id + "\n"; 284 out += "Level: " + level + "\n"; 285 out += "Stem Length: " + stemLength + "\n"; 286 out += "Temporal Depth: " + tempDepth + "\n"; 287 if (parent != null) { 288 out += "Parent: " + parent.id + "\n"; 289 out += "Siblings: "; 290 for (Iterator i = getSiblings().iterator(); i.hasNext();) { 291 Species sibling = (Species) i.next(); 292 if (sibling.id != id) out += sibling.id + ","; 293 } 294 out += "\n"; 295 } else { 296 out += "Parent: null \n"; 297 out += "Siblings: none \n"; 298 } 299 if (children != null) { 300 out += "Children: "; 301 for (int i = 0; i < children.length; i++) { 302 out += children[i].id + ","; 303 } 304 out += "\n"; 305 } else { 306 out += "Children: none \n"; 307 } 308 return out; 309 } 310 311 public String toString() { 312 String out = ""; 313 314 out += id + " [" + level + ", " + tempDepth + "]\n"; 315 316 return out; 317 } 318 319 /** 320 * This will remove this internal species, linking all children to 321 * our parent and adjusting their stem lengths accordingly. If 322 * this Species is a leaf, then it won't do anything. It returns 323 * true if the species is collapsed and false otherwise. If a 324 * species is collapsed, it's up to the calling routine to remove 325 * the spacies from the Tree. 326 */ 327 public boolean collapse() { 328 // nothing to do if this is a leaf 329 if (isLeaf()) return false; 330 331 // only collapse this species, if a single child 332 if (numChildren() == 1) { 333 // get our child 334 Species child = children[0]; 335 336 // add child to our parent 337 if (parent == null) { 338 // we are the root, so our child will not have a 339 // stem length or a parent 340 child.setStemLength(0.0); 341 child.setParent(null); 342 } else { 343 // adjust stem length to include our stem length 344 child.setStemLength(stemLength + child.getStemLength()); 345 346 // this will set our child's parent 347 parent.addChild(child); 348 349 // we're not root, so remove us from our parent 350 parent.removeChild(this); 351 } 352 353 return true; 354 } 355 356 return false; 357 } 358 359 //-------------------------------------------------------------------------- 360 // Tree Related Functions: These functions treat the current 361 // species as a node in a tree. 362 363 /** 364 * This resets the index used by nextChild(). This must be called 365 * before nextChild() is used to traverse a tree. 366 */ 367 public void resetChildIndex() { 368 if (numChildren() > 0) childIndex = 0; 369 else childIndex = -1; 370 } 371 372 /** 373 * This will return the true if this is the first child to be 374 * returned. It will return false if no children. 375 */ 376 public boolean firstChild() { 377 if (childIndex == 0) return true; 378 else return false; 379 } 380 381 /** This will return true if there are more children. */ 382 public boolean hasNextChild() { 383 if ((childIndex > -1) && (childIndex < children.length)) { 384 return true; 385 } else { 386 return false; 387 } 388 } 389 390 /** 391 * This will return the next child keeping track of the children 392 * returned. This can be used to traverse a tree in a recursive 393 * like fashion but without using a recursive routine. The method 394 * resetChildIndex() must be called prior to using this routine, 395 * to reset the index. 396 */ 397 public Species nextChild() { 398 if ((childIndex > -1) && (childIndex < children.length)) { 399 childIndex++; 400 return children[childIndex-1]; 401 } else { 402 return null; 403 } 404 } 405 406 /** 407 * This will print out this species and all children as a NEXUS 408 * formatted tree. This species will be the root of the tree. 409 * @XXX This will give a stack overflow for deep trees. 410 */ 411 public String printNexus() { 412 String out = ""; 413 414 if (! isLeaf()) { 415 // add children 416 out += "("; 417 out += children[0].printNexus(); 418 for (int i = 1; i < children.length; i++) { 419 out += ", " + children[i].printNexus(); 420 } 421 out += ")"; 422 } 423 424 // add self 425 out += id + ":" + stemLength; 426 427 return out; 428 } 429 430 /** 431 * This will print out the tree, one species per line, indenting 432 * each level. 433 * @XXX This will give a stack overflow for deep trees. 434 */ 435 public String printTree(String indent) { 436 String out = ""; 437 438 out += level + " " + indent + id + ": " + tempDepth + " : " + stemLength + "\n"; 439 if (children != null) { 440 for (int i = 0; i < children.length; i++) { 441 out += children[i].printTree(indent + ". "); 442 } 443 } 444 445 return out; 446 } 447 448 } // Species.java 449