jpayne@68: package tax; jpayne@68: jpayne@68: import java.io.Serializable; jpayne@68: import java.util.Comparator; jpayne@68: jpayne@68: import shared.Tools; jpayne@68: jpayne@68: /** jpayne@68: * Represents a taxonomic identifier, such as a specific genus. jpayne@68: * Includes the name, NCBI numeric id, parent id, and taxonomic level. jpayne@68: * @author Brian Bushnell jpayne@68: * @date Mar 6, 2015 jpayne@68: * jpayne@68: */ jpayne@68: public class TaxNode implements Serializable{ jpayne@68: jpayne@68: /** jpayne@68: * jpayne@68: */ jpayne@68: private static final long serialVersionUID = -4618526038942239246L; jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: /*---------------- Initialization ----------------*/ jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: public TaxNode(int id_, String name_){ jpayne@68: this(id_, -1, -1, -1, name_); jpayne@68: } jpayne@68: jpayne@68: public TaxNode(int id_, int parent_, int level_, int levelExtended_, String name_){ jpayne@68: id=id_; jpayne@68: pid=parent_; jpayne@68: level=level_; jpayne@68: levelExtended=levelExtended_; jpayne@68: setOriginalLevel(levelExtended); jpayne@68: name=name_; jpayne@68: } jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: /*---------------- Methods ----------------*/ jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: /** jpayne@68: * @param split jpayne@68: * @param idx jpayne@68: * @return True if the node's name matches the jpayne@68: */ jpayne@68: public boolean matchesName(String[] split, int idx, TaxTree tree) { jpayne@68: if(idx<0){return true;} jpayne@68: if(!split[idx].equalsIgnoreCase(name)){return false;} jpayne@68: return tree.getNode(pid).matchesName(split, idx-1, tree); jpayne@68: } jpayne@68: jpayne@68: @Override jpayne@68: public String toString(){ jpayne@68: return "("+id+","+pid+","+countRaw+","+countSum+",'"+levelStringExtended(false)+"',"+(canonical() ? "T" : "F")+",'"+name+"')"; jpayne@68: } jpayne@68: jpayne@68: public boolean equals(TaxNode b){ jpayne@68: if(id!=b.id || pid!=b.pid || levelExtended!=b.levelExtended || flag!=b.flag){return false;} jpayne@68: if(name==b.name){return true;} jpayne@68: if((name==null) != (b.name==null)){return false;} jpayne@68: return name.equals(b.name); jpayne@68: } jpayne@68: jpayne@68: public long incrementRaw(long amt){ jpayne@68: if(amt==0){return countRaw;} jpayne@68: if(verbose){System.err.println("incrementRaw("+amt+") node: "+this);} jpayne@68: countRaw+=amt; jpayne@68: assert(countRaw>=0) : "Overflow! "+countRaw+", "+amt; jpayne@68: return countRaw; jpayne@68: } jpayne@68: jpayne@68: public long incrementSum(long amt){ jpayne@68: if(amt==0){return countSum;} jpayne@68: if(verbose){System.err.println("incrementSum("+amt+") node: "+this);} jpayne@68: countSum+=amt; jpayne@68: assert(countSum>=0 || amt<0) : "Overflow! "+countSum+", "+amt; jpayne@68: return countSum; jpayne@68: } jpayne@68: jpayne@68: public boolean isSimple(){ jpayne@68: return TaxTree.isSimple(levelExtended); jpayne@68: } jpayne@68: jpayne@68: public boolean isSimple2(){ jpayne@68: return TaxTree.isSimple2(levelExtended); jpayne@68: } jpayne@68: jpayne@68: // public String levelString(){return level<0 ? "unknown" : TaxTree.levelToString(level);} jpayne@68: jpayne@68: public String levelStringExtended(boolean original){ jpayne@68: int x=(original ? originalLevel() : levelExtended); jpayne@68: return x<0 ? "unknown" : TaxTree.levelToStringExtended(x); jpayne@68: } jpayne@68: jpayne@68: public String levelToStringShort() {return level<0 ? "x" : TaxTree.levelToStringShort(level);} jpayne@68: jpayne@68: jpayne@68: jpayne@68: public boolean isUnclassified(){ jpayne@68: return name.startsWith("unclassified"); jpayne@68: } jpayne@68: jpayne@68: public boolean isEnvironmentalSample(){ jpayne@68: return name.startsWith("environmental"); jpayne@68: } jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: /*---------------- Nested Classes ----------------*/ jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: public static class CountComparator implements Comparator{ jpayne@68: jpayne@68: @Override jpayne@68: public int compare(TaxNode a, TaxNode b) { jpayne@68: long x=b.countSum-a.countSum; jpayne@68: // System.err.println("x="+x+" -> "+Tools.longToInt(x)); jpayne@68: if(x!=0){return Tools.longToInt(x);} jpayne@68: return a.levelExtended==b.levelExtended ? a.id-b.id : a.levelExtended-b.levelExtended; jpayne@68: } jpayne@68: jpayne@68: } jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: /*---------------- Getters ----------------*/ jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: @Override jpayne@68: public final int hashCode(){return id;} jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: public boolean canonical(){ jpayne@68: return (flag&CANON_MASK)==CANON_MASK; jpayne@68: } jpayne@68: jpayne@68: public boolean levelChanged(){ jpayne@68: return originalLevel()!=levelExtended; jpayne@68: } jpayne@68: jpayne@68: public int originalLevel(){ jpayne@68: int x=(int)(flag&ORIGINAL_LEVEL_MASK); jpayne@68: return x==ORIGINAL_LEVEL_MASK ? -1 : x; jpayne@68: } jpayne@68: jpayne@68: public boolean cellularOrganisms(){ jpayne@68: return id==TaxTree.CELLULAR_ORGANISMS_ID; jpayne@68: } jpayne@68: jpayne@68: // public int numChildren(){ jpayne@68: // return numChildren; jpayne@68: // } jpayne@68: // jpayne@68: // public int minParentLevelExtended(){ jpayne@68: // return minParentLevelExtended; jpayne@68: // } jpayne@68: // jpayne@68: // public int maxChildLevelExtended(){ jpayne@68: // return maxChildLevelExtended; jpayne@68: // } jpayne@68: jpayne@68: int minAncestorLevelIncludingSelf(){ jpayne@68: return levelExtended<1 ? minParentLevelExtended : levelExtended; jpayne@68: } jpayne@68: jpayne@68: int maxDescendantLevelIncludingSelf(){ jpayne@68: return levelExtended<1 ? maxChildLevelExtended : levelExtended; jpayne@68: } jpayne@68: jpayne@68: public String simpleName(){ jpayne@68: if(name==null){return null;} jpayne@68: StringBuilder sb=new StringBuilder(); jpayne@68: char last='?'; jpayne@68: for(int i=0; i='a' && c<='z') || (c>='A' && c<='Z') || (c>='1' && c<='0')){ jpayne@68: sb.append(c); jpayne@68: last=c; jpayne@68: }else{ jpayne@68: if(sb.length()>0 && last!=' '){sb.append(' ');} jpayne@68: last=' '; jpayne@68: } jpayne@68: } jpayne@68: String s=sb.toString().trim(); jpayne@68: return s.replace(' ', '_'); jpayne@68: } jpayne@68: jpayne@68: public boolean isRanked() {return levelExtended!=TaxTree.NO_RANK_E;} jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: /*---------------- Setters ----------------*/ jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: public void setCanonical(boolean b){ jpayne@68: if(b){flag=flag|CANON_MASK;} jpayne@68: else{flag=flag&~CANON_MASK;} jpayne@68: } jpayne@68: jpayne@68: public void setOriginalLevel(int x){ jpayne@68: flag=(flag&~ORIGINAL_LEVEL_MASK)|(x&ORIGINAL_LEVEL_MASK); jpayne@68: } jpayne@68: jpayne@68: /** Return true if changed */ jpayne@68: boolean discussWithParent(TaxNode parent){ jpayne@68: final int oldChildLevel=parent.maxChildLevelExtended; jpayne@68: final int oldParentLevel=minParentLevelExtended; jpayne@68: parent.maxChildLevelExtended=Tools.max(parent.maxChildLevelExtended, maxDescendantLevelIncludingSelf()); jpayne@68: minParentLevelExtended=Tools.min(parent.minAncestorLevelIncludingSelf(), minParentLevelExtended); jpayne@68: return oldChildLevel!=parent.maxChildLevelExtended || oldParentLevel!=minParentLevelExtended; jpayne@68: } jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: /*---------------- Fields ----------------*/ jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: public final int id; jpayne@68: public final String name; jpayne@68: public int pid; jpayne@68: public int level; jpayne@68: public int levelExtended; jpayne@68: jpayne@68: public int numChildren=0; jpayne@68: public int minParentLevelExtended=TaxTree.LIFE_E; jpayne@68: public int maxChildLevelExtended=TaxTree.NO_RANK_E; jpayne@68: jpayne@68: private long flag=0; jpayne@68: jpayne@68: public long countRaw=0; jpayne@68: public long countSum=0; jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: /*---------------- Constants ----------------*/ jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: private static final long ORIGINAL_LEVEL_MASK=63; //bits 0-5 jpayne@68: private static final long CANON_MASK=64; //bit 6 jpayne@68: jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: /*---------------- Statics ----------------*/ jpayne@68: /*--------------------------------------------------------------*/ jpayne@68: jpayne@68: public static final boolean verbose=false; jpayne@68: public static final CountComparator countComparator=new CountComparator(); jpayne@68: jpayne@68: jpayne@68: }