Java tutorial
/* * Copyright (c) 2008-2013 David Soergel <dev@davidsoergel.com> * Licensed under the Apache License, Version 2.0 * http://www.apache.org/licenses/LICENSE-2.0 */ package edu.berkeley.compbio.ncbitaxonomy.jpa; import com.davidsoergel.dsutils.collections.DSCollectionUtils; import com.davidsoergel.springjpautils.SpringJpaObject; import com.davidsoergel.trees.DepthFirstTreeIterator; import com.davidsoergel.trees.HierarchyNode; import com.davidsoergel.trees.NoSuchNodeException; import com.davidsoergel.trees.NodeNamer; import com.davidsoergel.trees.PhylogenyNode; import com.davidsoergel.trees.RootedPhylogeny; import edu.berkeley.compbio.ncbitaxonomy.dao.NcbiTaxonomyNodeDao; import org.apache.commons.lang.NotImplementedException; import org.apache.log4j.Logger; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Table; import javax.persistence.Transient; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * Represents a row of the "nodes" table in the NCBI taxonomy database. * * @author <a href="mailto:dev@davidsoergel.com">David Soergel</a> * @version $Id$ */ @Entity @Table(name = "nodes") @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @NamedQueries({ @NamedQuery(name = "NcbiTaxonomyNode.findByRank", query = "select n from NcbiTaxonomyNode n WHERE rank = :rank"), @NamedQuery(name = "NcbiTaxonomyNode.findIdsByRank", query = "select n.id from NcbiTaxonomyNode n WHERE rank = :rank"), @NamedQuery(name = "NcbiTaxonomyNode.findAllIds", query = "select n.id from NcbiTaxonomyNode n"), @NamedQuery(name = "NcbiTaxonomyNode.findChildIds", query = "select n.id from NcbiTaxonomyNode n WHERE parent_tax_id = :id") }) // or NONSTRICT_READ_WRITE? //@NamedQuery(name="NcbiTaxonomyNode.findByName",query="select n from NcbiTaxonomyNode n WHERE Name = :name"), public class NcbiTaxonomyNode extends SpringJpaObject implements PhylogenyNode<Integer> { // ------------------------------ FIELDS ------------------------------ @Autowired @Transient private NcbiTaxonomyNodeDao ncbiTaxonomyNodeDao; public void setName(final String name) { throw new NotImplementedException(); } public String getName() { throw new NotImplementedException(); } private static final Logger logger = Logger.getLogger(NcbiTaxonomyName.class); //private int taxId; @ManyToOne(fetch = FetchType.EAGER) //LAZY @JoinColumn(name = "parent_tax_id") private NcbiTaxonomyNode parent; // may as well be eager since we need these for the hashcode @OneToMany(mappedBy = "taxon", fetch = FetchType.EAGER) private Set<NcbiTaxonomyName> names; @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY) //, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) //, fetch = FetchType.EAGER) //, CascadeType.REFRESH}) //@LazyCollection(LazyCollectionOption.FALSE) //@Fetch(value = FetchMode.SUBSELECT) @OrderBy("id") private List<NcbiTaxonomyNode> children = new ArrayList<NcbiTaxonomyNode>(); @Column(name = "rank") private String rank; @Column(name = "embl_code") private String emblCode; @Column(name = "division_id") private int divisionId; @Column(name = "inherited_div_flag") private boolean inheritedDivFlag; @Column(name = "genetic_code_id") private int geneticCodeId; @Column(name = "inherited_GC_flag") private boolean inheritedGCFlag; @Column(name = "mitochondrial_genetic_code_id") private int mitachondrialGeneticCodeId; @Column(name = "inherited_MGC_flag") private boolean inheritedMGCFlag; @Column(name = "GenBank_hidden_flag") private boolean genBankHiddenFlag; @Column(name = "hidden_subtree_root_flag") private boolean hiddenSubtreeRootFlag; @Column(name = "comments") private String comments; private static final String SCIENTIFIC_NAME = "scientific name"; // --------------------- GETTER / SETTER METHODS --------------------- public String getComments() { return comments; } public void setComments(String comments) { this.comments = comments; } public int getDivisionId() { return divisionId; } public void setDivisionId(int divisionId) { this.divisionId = divisionId; } public String getEmblCode() { return emblCode; } public void setEmblCode(String emblCode) { this.emblCode = emblCode; } public int getGeneticCodeId() { return geneticCodeId; } public void setGeneticCodeId(int geneticCodeId) { this.geneticCodeId = geneticCodeId; } public Set<NcbiTaxonomyName> getNames() { return names; } public void setNames(Set<NcbiTaxonomyName> names) { this.names = names; } public long getTaxId() { return getId(); } /** * {@inheritDoc} */ @NotNull public Integer getPayload() { return getId(); } /** * {@inheritDoc} */ public NcbiTaxonomyNode getParent() { return parent; } public PhylogenyNode<Integer> findRoot() { if (parent == null) { return this; } else { return parent.findRoot(); } } /** * {@inheritDoc} */ public boolean hasValue() { return true; } /** * {@inheritDoc} */ /* public NcbiTaxonomyNode newChild() { throw new NotImplementedException("The NCBI taxonomy is not editable"); } */ /** * Not implemented */ public PhylogenyNode<Integer> newChild(Integer payload) { throw new NotImplementedException("The NCBI taxonomy is not editable"); } /** * {@inheritDoc} */ public void setPayload(Integer contents) { throw new NotImplementedException("The NCBI taxonomy is not editable"); } /** * {@inheritDoc} */ //** sketchy; should be setParent(NcbiTaxonomyNode parent) public void setParent(PhylogenyNode<Integer> parent) { if (this.parent != null) { this.parent.unregisterChild(this); } this.parent = (NcbiTaxonomyNode) parent; if (this.parent != null) { this.parent.registerChild(this); } } public String getRank() { return rank; } public void setRank(String rank) { this.rank = rank; } public boolean isGenBankHiddenFlag() { return genBankHiddenFlag; } public void setGenBankHiddenFlag(boolean genBankHiddenFlag) { this.genBankHiddenFlag = genBankHiddenFlag; } public boolean isHiddenSubtreeRootFlag() { return hiddenSubtreeRootFlag; } public void setHiddenSubtreeRootFlag(boolean hiddenSubtreeRootFlag) { this.hiddenSubtreeRootFlag = hiddenSubtreeRootFlag; } public boolean isInheritedDivFlag() { return inheritedDivFlag; } public void setInheritedDivFlag(boolean inheritedDivFlag) { this.inheritedDivFlag = inheritedDivFlag; } public boolean isInheritedGCFlag() { return inheritedGCFlag; } public void setInheritedGCFlag(boolean inheritedGCFlag) { this.inheritedGCFlag = inheritedGCFlag; } public boolean isInheritedMGCFlag() { return inheritedMGCFlag; } public void setInheritedMGCFlag(boolean inheritedMGCFlag) { this.inheritedMGCFlag = inheritedMGCFlag; } // ------------------------ CANONICAL METHODS ------------------------ //** Using the names collection here probably doesn't work // well, not with LAZY loading, but now that it's EAGER it should be OK // ** gah why can't equals and hashCode depend only on getId? // If there are ever transient nodes that don't have an ID yet, that'sa problem; but the NCBI taxonomy is read-only so that should never occur /* @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof NcbiTaxonomyNode)) { return false; } NcbiTaxonomyNode that = (NcbiTaxonomyNode) o; return this.getId().equals(that.getId()); // if (names != null ? !names.equals(that.names) : that.names != null) // { // return false; // } // if (parent != null ? !parent.equals(that.parent) : that.parent != null) // { // return false; // } // // return true; } @Override //@Transactional public int hashCode() { int result; result = getId(); // result = ((parent != null && !parent.getId().equals(this.getId())) ? parent.hashCode() : 0); // result = 31 * result + (names != null ? names.hashCode() : 0); return result; } */ // -------------------------- OTHER METHODS -------------------------- /** * {@inheritDoc} */ @NotNull public List<NcbiTaxonomyNode> getChildren() { return children; } /** * {@inheritDoc} */ @NotNull public PhylogenyNode<Integer> getChildWithPayload(Integer id) throws NoSuchNodeException { // We could map the children collection as a Map; but that's some hassle, and since there are generally just 2 children anyway, this is simpler for (NcbiTaxonomyNode child : children) { if (child.getId().equals(id)) { return child; } } throw new NoSuchNodeException("Could not find child: " + id); } public int getMitochondrialGeneticCodeId() { return mitachondrialGeneticCodeId; } public void setChildSets(List<NcbiTaxonomyNode> children) { this.children = children; } public void setMitochondrialGeneticCodeId(int mitachondrialGeneticCodeId) { this.mitachondrialGeneticCodeId = mitachondrialGeneticCodeId; } /** * Returns an iterator over a set of elements of type T. * * @return an Iterator. */ public Iterator<PhylogenyNode<Integer>> iterator() { throw new NotImplementedException("Iterating the entire NCBI taxonomy is probably a bad idea"); } public void addToMap(Map<Integer, PhylogenyNode<Integer>> uniqueIdToNodeMap, NodeNamer<Integer> namer) { throw new NotImplementedException("Iterating over the entire NCBI taxonomy is probably a bad idea"); } /** * {@inheritDoc} */ public DepthFirstTreeIterator<Integer, PhylogenyNode<Integer>> depthFirstIterator() { throw new NotImplementedException("Iterating the entire NCBI taxonomy is probably a bad idea"); } @Transient private List<PhylogenyNode<Integer>> ancestorPath; /** * {@inheritDoc} */ //@Transactional(propagation = Propagation.MANDATORY) public List<PhylogenyNode<Integer>> getAncestorPath() { if (ancestorPath == null) { fetchAncestorPath(); } return ancestorPath; } public List<? extends HierarchyNode<Integer, PhylogenyNode<Integer>>> getAncestorPath( final boolean includeSelf) { throw new NotImplementedException(); } @Transient private List<Integer> ancestorPathIds; /** * {@inheritDoc} */ //@Transactional(propagation = Propagation.MANDATORY) public List<Integer> getAncestorPathPayloads() { if (ancestorPathIds == null) { fetchAncestorPathIds(); } return ancestorPathIds; } //@Transactional(propagation = Propagation.MANDATORY) public void fetchAncestorPath() { // because the nodes.parent_id column is notnull, the root node is its own parent. // avoid infinite loop: //NcbiTaxonomyNode parent = getParent(); if (parent == null || getId().equals(parent.getId())) { ancestorPath = new LinkedList<PhylogenyNode<Integer>>(); //we're at the root } else { ancestorPath = new LinkedList<PhylogenyNode<Integer>>(getParent().getAncestorPath()); } ancestorPath.add(this); ancestorPath = Collections.unmodifiableList(ancestorPath); } //@Transactional(propagation = Propagation.MANDATORY) public void fetchAncestorPathIds() { ancestorPathIds = new LinkedList<Integer>(); for (PhylogenyNode<Integer> node : getAncestorPath()) { ancestorPathIds.add(node.getPayload()); } ancestorPathIds = Collections.unmodifiableList(ancestorPathIds); } /** * {@inheritDoc} */ @Nullable public Double getLength() { return null; //throw new NotImplementedException("The NCBI Taxonomy does not provide branch lengths."); } /** * {@inheritDoc} */ public void setLength(Double d) { throw new NotImplementedException("The NCBI Taxonomy does not provide branch lengths."); } /** * {@inheritDoc} */ //@Nullable public double getLargestLengthSpan() { //return null; throw new NotImplementedException("The NCBI Taxonomy does not provide branch lengths."); } /** * {@inheritDoc} */ //@Nullable public double getGreatestBranchLengthDepthBelow() { //return null; throw new NotImplementedException("The NCBI Taxonomy does not provide branch lengths."); } /** * {@inheritDoc} */ //@Nullable public double getLeastBranchLengthDepthBelow() { //return null; throw new NotImplementedException("The NCBI Taxonomy does not provide branch lengths."); } /** * Not implemented */ public void registerChild(PhylogenyNode<Integer> a) { throw new NotImplementedException(); } /** * Not implemented */ public void unregisterChild(PhylogenyNode<Integer> a) { throw new NotImplementedException(); } /** * {@inheritDoc} */ @Transactional(propagation = Propagation.MANDATORY) public boolean isLeaf() { return getChildren().isEmpty(); } /** * {@inheritDoc} */ public Double getWeight() { //throw new NotImplementedException("The NCBI Taxonomy does not provide branch weights."); return null; } /** * {@inheritDoc} */ public Double getCurrentWeight() { throw new NotImplementedException("The NCBI Taxonomy does not provide branch weights."); } /** * {@inheritDoc} */ public void setWeight(@NotNull Double d) { throw new NotImplementedException("The NCBI Taxonomy does not provide branch weights."); } public void setWeight(double v) { throw new NotImplementedException("The NCBI Taxonomy does not provide branch weights."); } /** * {@inheritDoc} */ public void incrementWeightBy(double v) { throw new NotImplementedException("The NCBI Taxonomy does not provide branch weights."); } /*public void propagateWeightFromBelow() { throw new NotImplementedException("The NCBI Taxonomy does not provide weights."); }*/ /** * {@inheritDoc} */ public double distanceToRoot() { throw new NotImplementedException("The NCBI Taxonomy does not provide branch lengths."); } public PhylogenyNode<Integer> nearestAncestorWithBranchLength() { throw new NotImplementedException("The NCBI Taxonomy does not provide branch lengths."); } /** * {@inheritDoc} */ @Override public RootedPhylogeny<Integer> clone() { throw new NotImplementedException(); } /** * {@inheritDoc} */ public NcbiTaxonomyNode getSelfNode() { return this; } /** * Not implemented */ public void appendSubtree(StringBuffer sb, String indent) { throw new NotImplementedException("Loading the entire NCBI taxonomy into a String is probably a bad idea"); } public int countDescendantsIncludingThis() { //return 0; throw new NotImplementedException("Counting subtrees in the entire NCBI taxonomy is probably a bad idea"); } public String toString() { return getTaxId() + "/" + getScientificName(); } public String getScientificName() { for (NcbiTaxonomyName name : names) { if (name.getNameClass().equals(SCIENTIFIC_NAME)) { return name.getName(); } } return "No Scientific Name Available"; } // cut & paste, too bad public void toNewick(Writer out, String prefix, String tab, int minClusterSize, double minLabelProb) throws IOException { throw new NotImplementedException(); } public RootedPhylogeny<Integer> asRootedPhylogeny() { throw new NotImplementedException(); } /** * Note this samples from the distribution of leaves weighted by the tree structure, i.e. uniformly _per level_, not * uniformly from the set of leaves. Basically, leaves with fewer siblings and cousins are more likely to be chosen. * * @return */ public PhylogenyNode<Integer> getRandomLeafBelow() { // iterate, don't recurse, in case the tree is deep PhylogenyNode<Integer> trav = this; List<? extends PhylogenyNode<Integer>> travChildren = trav.getChildren(); while (!travChildren.isEmpty()) { trav = DSCollectionUtils.chooseRandom(travChildren); travChildren = trav.getChildren(); } return trav; } public void collectLeavesBelowAtApproximateDistance(final double minDesiredTreeDistance, final double maxDesiredTreeDistance, final Collection<PhylogenyNode<Integer>> result) { throw new NotImplementedException("The NCBI Taxonomy does not provide branch lengths."); } @NotNull public Collection<? extends PhylogenyNode<Integer>> getDescendantLeaves() { Set<PhylogenyNode<Integer>> result = new HashSet<PhylogenyNode<Integer>>(); for (PhylogenyNode<Integer> n : this) { if (n.isLeaf()) { result.add(n); } } return result; } }