package org.iplantc.phyloviewer.viewer.server.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import org.iplantc.phyloviewer.shared.model.INode;
import org.iplantc.phyloviewer.shared.model.Tree;
import org.iplantc.phyloviewer.viewer.client.model.RemoteNode;
public class ImportTree {
Connection connection;
PreparedStatement addTreeStmt = null;
PreparedStatement addNodeStmt = null;
PreparedStatement addChildStmt = null;
PreparedStatement getNodeIdStmt = null;
PreparedStatement addAltLabelStmt = null;
public ImportTree(Connection conn) throws SQLException {
this.connection = conn;
// Create our prepared statements.
addNodeStmt = conn.prepareStatement("insert into node(Label) values (?)");
addChildStmt = conn.prepareStatement("insert into topology (node_id, parent_id, tree_id, NumNodes, NumLeaves, Height, LeftNode, RightNode, Depth, NumChildren) values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
String sql = "insert into tree(root_id,Name) values(?, ?)";
addTreeStmt = conn.prepareStatement(sql);
getNodeIdStmt = conn.prepareStatement("select currval('nodes_node_id') as result"); //TODO: I think PreparedStatement can return the sequence number without a query, using Statement.RETURN_GENERATED_KEYS
addAltLabelStmt = conn.prepareStatement("insert into node_label_lookup(node_id,alt_label) values(?, ?)");
}
public void close() {
ConnectionUtil.close(addTreeStmt);
ConnectionUtil.close(addNodeStmt);
ConnectionUtil.close(addChildStmt);
ConnectionUtil.close(getNodeIdStmt);
ConnectionUtil.close(addAltLabelStmt);
}
public void addTree(Tree tree,String name) throws SQLException
{
INode root = tree.getRootNode();
try
{
// We need to add the root first to meet the key constraints of the database.
addNode(root);
addTreeStmt.setInt(1, tree.getRootNode().getId());
addTreeStmt.setString(2, name != null ? name : "No name");
addTreeStmt.execute();
{
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery("select currval('trees_tree_id') as result" );
if (rs.next()) {
tree.setId(rs.getInt("result"));
}
}
addChildStmt.setInt(3, tree.getId());
if (root instanceof RemoteNode)
{
//add the tree root to the topology table
addChild(null, (RemoteNode) root, addChildStmt);
// add the children of the root.
if (root.getChildren() != null)
{
for (RemoteNode child : (RemoteNode[]) root.getChildren())
{
addSubtree(child, root.getId(), addNodeStmt, addChildStmt);
}
}
}
else
{
//if not RemoteNode, the depth and left and right indices have to be calculated in the traversal
int left = 1;
int right = 2 * root.getNumberOfNodes();
int depth = 0;
addChild(null, root, left, right, depth, addChildStmt);
// add the children of the root.
if (root.getChildren() != null)
{
for (INode child : root.getChildren())
{
addSubtree(child, root.getId(), left + 1, depth + 1, addNodeStmt, addChildStmt);
}
}
}
addChildStmt.executeBatch();
}
catch(SQLException e)
{
// Rethrow.
throw e;
}
finally
{
this.close();
}
}
private int addSubtree(INode node, int parentID, int traversalCount, int depth, PreparedStatement addNodeStmt, PreparedStatement addChildStmt) throws SQLException
{
addNode(node);
int left = traversalCount;
traversalCount++;
if (node.getChildren() != null)
{
for (INode child : node.getChildren())
{
traversalCount = addSubtree(child, node.getId(), traversalCount, depth + 1, addNodeStmt, addChildStmt);
}
}
int right = traversalCount;
traversalCount++;
addChild(parentID, node, left, right, depth, addChildStmt);
return traversalCount;
}
private void addSubtree(RemoteNode node, int parentID, PreparedStatement addNodeStmt, PreparedStatement addChildStmt) throws SQLException
{
addNode(node);
if (node.getChildren() != null)
{
for (RemoteNode child : node.getChildren())
{
addSubtree(child, node.getId(), addNodeStmt, addChildStmt);
}
}
addChild(parentID, node, addChildStmt);
}
private void addNode(INode node) throws SQLException
{
String label = node.getLabel();
addNodeStmt.setString(1, label);
addNodeStmt.execute();
ResultSet rs = getNodeIdStmt.executeQuery();
if (rs.next()) {
node.setId(rs.getInt("result"));
}
if (label==null) {
label = node.findLabelOfFirstLeafNode();
addAltLabelStmt.setInt(1, node.getId());
addAltLabelStmt.setString(2, label);
addAltLabelStmt.execute();
}
}
private void addChild(Integer parentID, INode child, int left, int right, int depth, PreparedStatement addChildStmt) throws SQLException
{
addChildStmt.setInt(1, child.getId());
if (parentID != null)
{
addChildStmt.setInt(2, parentID);
}
else
{
addChildStmt.setNull(2, Types.INTEGER);
}
//param 3 (treeID) is already set by addTree
addChildStmt.setInt(4, child.getNumberOfNodes());
addChildStmt.setInt(5, child.getNumberOfLeafNodes());
addChildStmt.setInt(6, child.findMaximumDepthToLeaf());
addChildStmt.setInt(7, left);
addChildStmt.setInt(8, right);
addChildStmt.setInt(9, depth);
addChildStmt.setInt(10, child.getNumberOfChildren());
addChildStmt.addBatch();
}
private void addChild(Integer parentID, RemoteNode child, PreparedStatement addChildStmt) throws SQLException
{
addChildStmt.setInt(1, child.getId());
if (parentID != null)
{
addChildStmt.setInt(2, parentID);
}
else
{
addChildStmt.setNull(2, Types.INTEGER);
}
//param 3 (treeID) is already set by addTree
addChildStmt.setInt(4, child.getNumberOfNodes());
addChildStmt.setInt(5, child.getNumberOfLeafNodes());
addChildStmt.setInt(6, child.findMaximumDepthToLeaf());
addChildStmt.setInt(7, child.getLeftIndex());
addChildStmt.setInt(8, child.getRightIndex());
addChildStmt.setInt(9, child.getDepth());
addChildStmt.setInt(10, child.getNumberOfChildren());
addChildStmt.addBatch();
}
}