1 package tax; 2 3 import java.io.Serializable; 4 import java.util.Comparator; 5 6 import shared.Tools; 7 8 /** 9 * Represents a taxonomic identifier, such as a specific genus. 10 * Includes the name, NCBI numeric id, parent id, and taxonomic level. 11 * @author Brian Bushnell 12 * @date Mar 6, 2015 13 * 14 */ 15 public class TaxNode implements Serializable{ 16 17 /** 18 * 19 */ 20 private static final long serialVersionUID = -4618526038942239246L; 21 22 /*--------------------------------------------------------------*/ 23 /*---------------- Initialization ----------------*/ 24 /*--------------------------------------------------------------*/ 25 TaxNode(int id_, String name_)26 public TaxNode(int id_, String name_){ 27 this(id_, -1, -1, -1, name_); 28 } 29 TaxNode(int id_, int parent_, int level_, int levelExtended_, String name_)30 public TaxNode(int id_, int parent_, int level_, int levelExtended_, String name_){ 31 id=id_; 32 pid=parent_; 33 level=level_; 34 levelExtended=levelExtended_; 35 setOriginalLevel(levelExtended); 36 name=name_; 37 } 38 39 /*--------------------------------------------------------------*/ 40 /*---------------- Methods ----------------*/ 41 /*--------------------------------------------------------------*/ 42 43 /** 44 * @param split 45 * @param idx 46 * @return True if the node's name matches the 47 */ matchesName(String[] split, int idx, TaxTree tree)48 public boolean matchesName(String[] split, int idx, TaxTree tree) { 49 if(idx<0){return true;} 50 if(!split[idx].equalsIgnoreCase(name)){return false;} 51 return tree.getNode(pid).matchesName(split, idx-1, tree); 52 } 53 54 @Override toString()55 public String toString(){ 56 return "("+id+","+pid+","+countRaw+","+countSum+",'"+levelStringExtended(false)+"',"+(canonical() ? "T" : "F")+",'"+name+"')"; 57 } 58 equals(TaxNode b)59 public boolean equals(TaxNode b){ 60 if(id!=b.id || pid!=b.pid || levelExtended!=b.levelExtended || flag!=b.flag){return false;} 61 if(name==b.name){return true;} 62 if((name==null) != (b.name==null)){return false;} 63 return name.equals(b.name); 64 } 65 incrementRaw(long amt)66 public long incrementRaw(long amt){ 67 if(amt==0){return countRaw;} 68 if(verbose){System.err.println("incrementRaw("+amt+") node: "+this);} 69 countRaw+=amt; 70 assert(countRaw>=0) : "Overflow! "+countRaw+", "+amt; 71 return countRaw; 72 } 73 incrementSum(long amt)74 public long incrementSum(long amt){ 75 if(amt==0){return countSum;} 76 if(verbose){System.err.println("incrementSum("+amt+") node: "+this);} 77 countSum+=amt; 78 assert(countSum>=0 || amt<0) : "Overflow! "+countSum+", "+amt; 79 return countSum; 80 } 81 isSimple()82 public boolean isSimple(){ 83 return TaxTree.isSimple(levelExtended); 84 } 85 isSimple2()86 public boolean isSimple2(){ 87 return TaxTree.isSimple2(levelExtended); 88 } 89 90 // public String levelString(){return level<0 ? "unknown" : TaxTree.levelToString(level);} 91 levelStringExtended(boolean original)92 public String levelStringExtended(boolean original){ 93 int x=(original ? originalLevel() : levelExtended); 94 return x<0 ? "unknown" : TaxTree.levelToStringExtended(x); 95 } 96 levelToStringShort()97 public String levelToStringShort() {return level<0 ? "x" : TaxTree.levelToStringShort(level);} 98 99 100 isUnclassified()101 public boolean isUnclassified(){ 102 return name.startsWith("unclassified"); 103 } 104 isEnvironmentalSample()105 public boolean isEnvironmentalSample(){ 106 return name.startsWith("environmental"); 107 } 108 109 /*--------------------------------------------------------------*/ 110 /*---------------- Nested Classes ----------------*/ 111 /*--------------------------------------------------------------*/ 112 113 public static class CountComparator implements Comparator<TaxNode>{ 114 115 @Override compare(TaxNode a, TaxNode b)116 public int compare(TaxNode a, TaxNode b) { 117 long x=b.countSum-a.countSum; 118 // System.err.println("x="+x+" -> "+Tools.longToInt(x)); 119 if(x!=0){return Tools.longToInt(x);} 120 return a.levelExtended==b.levelExtended ? a.id-b.id : a.levelExtended-b.levelExtended; 121 } 122 123 } 124 125 /*--------------------------------------------------------------*/ 126 /*---------------- Getters ----------------*/ 127 /*--------------------------------------------------------------*/ 128 129 @Override hashCode()130 public final int hashCode(){return id;} 131 132 /*--------------------------------------------------------------*/ 133 canonical()134 public boolean canonical(){ 135 return (flag&CANON_MASK)==CANON_MASK; 136 } 137 levelChanged()138 public boolean levelChanged(){ 139 return originalLevel()!=levelExtended; 140 } 141 originalLevel()142 public int originalLevel(){ 143 int x=(int)(flag&ORIGINAL_LEVEL_MASK); 144 return x==ORIGINAL_LEVEL_MASK ? -1 : x; 145 } 146 cellularOrganisms()147 public boolean cellularOrganisms(){ 148 return id==TaxTree.CELLULAR_ORGANISMS_ID; 149 } 150 151 // public int numChildren(){ 152 // return numChildren; 153 // } 154 // 155 // public int minParentLevelExtended(){ 156 // return minParentLevelExtended; 157 // } 158 // 159 // public int maxChildLevelExtended(){ 160 // return maxChildLevelExtended; 161 // } 162 minAncestorLevelIncludingSelf()163 int minAncestorLevelIncludingSelf(){ 164 return levelExtended<1 ? minParentLevelExtended : levelExtended; 165 } 166 maxDescendantLevelIncludingSelf()167 int maxDescendantLevelIncludingSelf(){ 168 return levelExtended<1 ? maxChildLevelExtended : levelExtended; 169 } 170 simpleName()171 public String simpleName(){ 172 if(name==null){return null;} 173 StringBuilder sb=new StringBuilder(); 174 char last='?'; 175 for(int i=0; i<name.length(); i++){ 176 char c=name.charAt(i); 177 if((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='1' && c<='0')){ 178 sb.append(c); 179 last=c; 180 }else{ 181 if(sb.length()>0 && last!=' '){sb.append(' ');} 182 last=' '; 183 } 184 } 185 String s=sb.toString().trim(); 186 return s.replace(' ', '_'); 187 } 188 isRanked()189 public boolean isRanked() {return levelExtended!=TaxTree.NO_RANK_E;} 190 191 /*--------------------------------------------------------------*/ 192 /*---------------- Setters ----------------*/ 193 /*--------------------------------------------------------------*/ 194 setCanonical(boolean b)195 public void setCanonical(boolean b){ 196 if(b){flag=flag|CANON_MASK;} 197 else{flag=flag&~CANON_MASK;} 198 } 199 setOriginalLevel(int x)200 public void setOriginalLevel(int x){ 201 flag=(flag&~ORIGINAL_LEVEL_MASK)|(x&ORIGINAL_LEVEL_MASK); 202 } 203 204 /** Return true if changed */ discussWithParent(TaxNode parent)205 boolean discussWithParent(TaxNode parent){ 206 final int oldChildLevel=parent.maxChildLevelExtended; 207 final int oldParentLevel=minParentLevelExtended; 208 parent.maxChildLevelExtended=Tools.max(parent.maxChildLevelExtended, maxDescendantLevelIncludingSelf()); 209 minParentLevelExtended=Tools.min(parent.minAncestorLevelIncludingSelf(), minParentLevelExtended); 210 return oldChildLevel!=parent.maxChildLevelExtended || oldParentLevel!=minParentLevelExtended; 211 } 212 213 /*--------------------------------------------------------------*/ 214 /*---------------- Fields ----------------*/ 215 /*--------------------------------------------------------------*/ 216 217 public final int id; 218 public final String name; 219 public int pid; 220 public int level; 221 public int levelExtended; 222 223 public int numChildren=0; 224 public int minParentLevelExtended=TaxTree.LIFE_E; 225 public int maxChildLevelExtended=TaxTree.NO_RANK_E; 226 227 private long flag=0; 228 229 public long countRaw=0; 230 public long countSum=0; 231 232 /*--------------------------------------------------------------*/ 233 /*---------------- Constants ----------------*/ 234 /*--------------------------------------------------------------*/ 235 236 private static final long ORIGINAL_LEVEL_MASK=63; //bits 0-5 237 private static final long CANON_MASK=64; //bit 6 238 239 /*--------------------------------------------------------------*/ 240 /*---------------- Statics ----------------*/ 241 /*--------------------------------------------------------------*/ 242 243 public static final boolean verbose=false; 244 public static final CountComparator countComparator=new CountComparator(); 245 246 247 } 248