package org.genedb.web.tags.db;
import static javax.servlet.jsp.PageContext.APPLICATION_SCOPE;
import static org.genedb.web.mvc.controller.TaxonManagerListener.TAXON_NODE_MANAGER;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import org.apache.commons.lang.StringUtils;
import org.genedb.db.taxon.TaxonNode;
import org.genedb.db.taxon.TaxonNodeManager;
/**
*
* @author larry@sangerinstitute The Purpose of this class is to build a tree of
* taxonomy for the Quick Search Function
*
*/
public class QuickSearchTaxonomicGraphTag extends SimpleTagSupport {
private String top = "Root"; // FIXME
private String currentTaxonNodeName;
private TreeMap<String, Integer> taxonGroup;
private String hasResult;
private String searchText;
private String allNames;
private String pseudogenes;
private String product;
private String baseUrl;
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
@SuppressWarnings("unchecked")
@Override
public void doTag() throws JspException, IOException {
PageContext pageContext = (PageContext) getJspContext();
TaxonNodeManager tnm = (TaxonNodeManager) getJspContext().getAttribute(TAXON_NODE_MANAGER, APPLICATION_SCOPE);
// currentTaxonNodeName = pageContext.getRequest().getParameter("taxons");
//
// taxonGroup = (TreeMap) getJspContext().findAttribute("taxonGroup");
//
// hasResult = pageContext.getRequest().getParameter("hasresults");
boolean displayAllMatchingTaxonsWhenResultsEmpty = taxonGroup != null && taxonGroup.size() > 0
&& hasResult != null && hasResult.equals("false");
TaxonNode currentNode = null;
if (StringUtils.isEmpty(currentTaxonNodeName) || displayAllMatchingTaxonsWhenResultsEmpty) {
currentNode = tnm.getTaxonNodeForLabel(top);
if (currentNode == null) {
throw new JspException("Homepage Tag: Can't identify taxonNode for '" + top + "'");
}
} else {
currentNode = tnm.getTaxonNodeForLabel(currentTaxonNodeName);
}
// Create the graph to be populated and manipulated
QuickSearchTaxonNode quickSearchTaxonNode = new QuickSearchTaxonNode();
// populate with taxons
buildTree(quickSearchTaxonNode, currentNode, taxonGroup);
// sort in taxonomic order of closeness to the organism sought after
if (displayAllMatchingTaxonsWhenResultsEmpty) {
sortInOrderOfCurrentTaxon(currentTaxonNodeName, quickSearchTaxonNode);
}
// Get the writer
JspWriter out = getJspContext().getOut();
String contextPath = ((HttpServletRequest) pageContext.getRequest()).getContextPath();
String htmlList = transform(quickSearchTaxonNode, contextPath);
out.write(htmlList);
}
/**
* Build the taxon tree as is represented in the Taxon Manager
*
* @param quickSearchTaxonNode
* @param taxonNode
* @param taxonGroup
*/
public void buildTree(QuickSearchTaxonNode quickSearchTaxonNode, TaxonNode taxonNode,
TreeMap<String, Integer> taxonGroup) {
// populate label
quickSearchTaxonNode.setLabel(taxonNode.getLabel());
if (taxonGroup.containsKey(taxonNode.getLabel())) {
quickSearchTaxonNode.setMatch(taxonGroup.get(taxonNode.getLabel()));
}
// populate children
for (TaxonNode childNode : taxonNode.getChildren()) {
QuickSearchTaxonNode myChild = new QuickSearchTaxonNode();
quickSearchTaxonNode.getChildren().add(myChild);
myChild.setParent(quickSearchTaxonNode);
buildTree(myChild, childNode, taxonGroup);
}
// Sort list
Collections.sort(quickSearchTaxonNode.getChildren(), new Comparator<QuickSearchTaxonNode>() {
@Override
public int compare(QuickSearchTaxonNode arg0, QuickSearchTaxonNode arg1) {
String label0 = arg0.getLabel();
String label1 = arg1.getLabel();
return label0.compareToIgnoreCase(label1);
}
});
}
/**
* Find the node in the tree whose label/name is given
*
* @param taxonNodeName
* @param tree
* @return
*/
private QuickSearchTaxonNode findCurrentQuickTaxonNode(String taxonNodeName, QuickSearchTaxonNode tree) {
if (tree.getLabel().equals(taxonNodeName)) {
return tree;
}
for (QuickSearchTaxonNode child : tree.getChildren()) {
QuickSearchTaxonNode found = findCurrentQuickTaxonNode(taxonNodeName, child);
if (found != null) {
return found;
}
}
return null;
}
/**
* Sort by re-arranging the sibling taxon nodes,by making each relevant node
* the first in line of siblings because the left-most node always appear on
* top of tree display
*
* @param currentTaxonNodeName
* @param quickSearchTaxonNode
*/
private void sortInOrderOfCurrentTaxon(String currentTaxonNodeName, QuickSearchTaxonNode quickSearchTaxonNode) {
QuickSearchTaxonNode currentNode = findCurrentQuickTaxonNode(currentTaxonNodeName, quickSearchTaxonNode);
sortInOrderOfCurrentTaxon(currentNode);
}
/**
* Re-arrange the siblings, make the current node or it's parent, the first
* in line
*
* @param quickSearchTaxonNode
*/
private void sortInOrderOfCurrentTaxon(QuickSearchTaxonNode quickSearchTaxonNode) {
QuickSearchTaxonNode parent = quickSearchTaxonNode.getParent();
if (parent != null) {
parent.getChildren().remove(quickSearchTaxonNode);
parent.getChildren().add(0, quickSearchTaxonNode);
sortInOrderOfCurrentTaxon(parent);
}
}
/**
* Transform taxons to String
*
* @param quickSearchTaxonNode
* @param sb
*/
public String transform(QuickSearchTaxonNode quickSearchTaxonNode, String contextPath) {
String tree = "";
for (QuickSearchTaxonNode child : quickSearchTaxonNode.getChildren()) {
tree = tree + transform(child, contextPath);
}
// Get the leafs where a match is found
if (quickSearchTaxonNode.getChildren().size() == 0 && quickSearchTaxonNode.getMatch() != 0) {
tree = "<li>" + createUrlHref(quickSearchTaxonNode, contextPath) + "</li>\n";
// Get parent nodes where a descendant has a match
} else if (isMatchFoundInDescendant(quickSearchTaxonNode)) {
String label = quickSearchTaxonNode.getLabel();
if (label != null && label.equalsIgnoreCase("root")) {
tree = "<i>All Organisms</i>\n" + "<ul>\n" + tree + "\n</ul>";
} else {
tree = "<li><i>" + label + "</i>\n" + "<ul>\n" + tree + "\n</ul>" + "</li>";
}
}
return tree;
}
/**
* Find a descendant with a match
*
* @param ancestor
* @return
*/
public boolean isMatchFoundInDescendant(QuickSearchTaxonNode ancestor) {
for (QuickSearchTaxonNode child : ancestor.getChildren()) {
if (child.getMatch() > 0) {
return true;
}
if (isMatchFoundInDescendant(child)) {
return true;
}
}
return false;
}
/**
* Create the URL for target _parent
*
* @param value
* @return
*/
private String createUrlHref(QuickSearchTaxonNode quickSearchTaxonNode, String contextPath) {
StringBuilder sb = new StringBuilder();
sb.append("<a href=\"");
sb.append(baseUrl);
//sb.append(contextPath);
sb.append("Query/quickSearch");
//sb.append("?q=quickSearchQuery");
sb.append("?taxons=");
sb.append(quickSearchTaxonNode.getLabel());
sb.append("&searchText=");
sb.append(searchText);
sb.append("&allNames=");
sb.append(allNames);
sb.append("&pseudogenes=");
sb.append(pseudogenes);
sb.append("&product=");
sb.append(product);
sb.append("\"");
sb.append(" target=\"_parent\">");
sb.append("<small>");
sb.append(reformatLabel(quickSearchTaxonNode.getLabel()));
sb.append("</small>");
sb.append("</a>");
sb.append("<small>");
sb.append("(");
sb.append(quickSearchTaxonNode.getMatch());
sb.append(")");
sb.append("</small>");
return sb.toString();
}
/**
* Re-format label with a dot after first character of the Organism name
* @param displayLabel
* @return
*/
private String reformatLabel(String displayLabel){
StringBuffer sb = new StringBuffer(displayLabel);
sb.insert(1, ". ");
return sb.toString();
}
private class QuickSearchTaxonNode {
private String label;
private int match;
private QuickSearchTaxonNode parent;
private List<QuickSearchTaxonNode> children = new ArrayList<QuickSearchTaxonNode>();
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public int getMatch() {
return match;
}
public void setMatch(int match) {
this.match = match;
}
public QuickSearchTaxonNode getParent() {
return parent;
}
public void setParent(QuickSearchTaxonNode parent) {
this.parent = parent;
}
public List<QuickSearchTaxonNode> getChildren() {
return children;
}
public void setChildren(List<QuickSearchTaxonNode> children) {
this.children = children;
}
public String toString() {
if (children.size() == 0) {
return String.format("<li>%s(%d)</li>", label, match);
} else {
String values = null;
for (QuickSearchTaxonNode node : children) {
values = node.toString() + "\n";
}
return String.format("<ul>\n%s</ul>", values);
}
}
}
public void setCurrentTaxonNodeName(String currentTaxonNodeName) {
this.currentTaxonNodeName = currentTaxonNodeName;
}
public void setTaxonGroup(TreeMap<String, Integer> taxonGroup) {
this.taxonGroup = taxonGroup;
}
public void setHasResult(String hasResult) {
this.hasResult = hasResult;
}
public void setTop(String top) {
this.top = top;
}
public void setSearchText(String searchText) {
this.searchText = searchText;
}
public void setAllNames(String allNames) {
this.allNames = allNames;
}
public void setPseudogenes(String pseudogenes) {
this.pseudogenes = pseudogenes;
}
public void setProduct(String product) {
this.product = product;
}
}