package org.iplantc.phyloviewer.viewer.server.db;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.sql.DataSource;
import org.iplantc.phyloparser.exception.ParserException;
import org.iplantc.phyloparser.model.FileData;
import org.iplantc.phyloparser.model.Node;
import org.iplantc.phyloparser.model.block.Block;
import org.iplantc.phyloparser.model.block.TreesBlock;
import org.iplantc.phyloviewer.server.render.ImageGraphics;
import org.iplantc.phyloviewer.shared.layout.ILayoutData;
import org.iplantc.phyloviewer.shared.layout.LayoutCladogram;
import org.iplantc.phyloviewer.shared.math.Matrix33;
import org.iplantc.phyloviewer.shared.model.Document;
import org.iplantc.phyloviewer.shared.model.Tree;
import org.iplantc.phyloviewer.shared.render.RenderTreeCladogram;
import org.iplantc.phyloviewer.viewer.client.model.RemoteNode;
import org.iplantc.phyloviewer.viewer.server.IImportTreeData;
public class ImportTreeData implements IImportTreeData {
private DataSource pool;
private String imageDirectory;
public ImportTreeData(DataSource pool,String imageDirectory) {
this.pool = pool;
this.imageDirectory = imageDirectory;
new File(imageDirectory).mkdir();
}
public static RemoteNode rootNodeFromNewick(String newick, String name) {
org.iplantc.phyloparser.parser.NewickParser parser = new org.iplantc.phyloparser.parser.NewickParser();
FileData data = null;
try {
data = parser.parse(newick);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
org.iplantc.phyloparser.model.Tree tree = null;
List<Block> blocks = data.getBlocks();
for ( Block block : blocks ) {
if ( block instanceof TreesBlock ) {
TreesBlock trees = (TreesBlock) block;
tree = trees.getTrees().get( 0 );
}
}
int depth = 0;
int nextTraversalIndex = 1; //starts with 1 to avoid ambiguity, because JDBC ResultSet.getInt() returns 0 for null values
return convertDataModels(tree.getRoot(), depth, nextTraversalIndex);
}
private static BufferedImage renderTreeImage(Tree tree, ILayoutData layout,
int width, int height) {
ImageGraphics graphics = new ImageGraphics(width, height);
RenderTreeCladogram renderer = new RenderTreeCladogram();
renderer.getRenderPreferences().setCollapseOverlaps(false);
renderer.getRenderPreferences().setDrawLabels(false);
renderer.getRenderPreferences().setDrawPoints(false);
Document document = new Document();
document.setTree(tree);
document.setLayout(layout);
renderer.setDocument(document);
renderer.renderTree(graphics, Matrix33.makeScale(width, height));
return graphics.getImage();
}
public int importTreeData(RemoteNode root, String name) {
Tree tree = new Tree();
tree.setRootNode(root);
Connection connection = null;
ImportLayout layoutImporter = null;
try
{
connection = pool.getConnection();
// The tree and all associated data will be added in a single transaction.
connection.setAutoCommit(false);
ImportTree importer = new ImportTree(connection);
importer.addTree(tree, name);
layoutImporter = new ImportLayout(connection);
LayoutCladogram cladogramLayout = new LayoutCladogram(0.8,1.0);
cladogramLayout.layout(tree);
{
String uuid = "LAYOUT_TYPE_CLADOGRAM";
layoutImporter.addLayout(uuid, cladogramLayout, tree);
BufferedImage image = renderTreeImage(tree,cladogramLayout,256,1024);
this.putOverviewImage(connection,tree.getId(), uuid, image);
}
connection.commit();
}
catch(SQLException e)
{
// TODO: Return a value to the user indicating an error.
String message = "Exception caught while trying to import a tree: " + e.getMessage();
Logger.getLogger("org.iplantc.phyloviewer").log(Level.SEVERE, message );
e.printStackTrace();
//rolls back entire tree transaction on exception anywhere in the tree
ConnectionUtil.rollback(connection);
}
finally
{
ConnectionUtil.close(connection);
if(layoutImporter != null) {
layoutImporter.close();
}
}
return tree.getId();
}
private void putOverviewImage(Connection connection, int treeId, String layoutId,
BufferedImage image) throws SQLException {
PreparedStatement statement = null;
try {
String filename = UUID.randomUUID().toString();
String path = imageDirectory+"/"+filename+".png";
File file = new File(path);
try {
ImageIO.write(image, "png", file);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
statement = connection.prepareStatement("insert into overview_images (tree_id,layout_id,image_width,image_height,image_path) values (?,?,?,?,?)");
statement.setInt(1, treeId);
statement.setString(2, layoutId);
statement.setInt(3, image.getWidth());
statement.setInt(4, image.getHeight());
statement.setString(5, path);
statement.executeUpdate();
ConnectionUtil.close(statement);
}
finally
{
ConnectionUtil.close(statement);
}
}
private static RemoteNode convertDataModels(org.iplantc.phyloparser.model.Node node, int depth, int nextTraversalIndex) {
List<Node> myChildren = node.getChildren();
int len = myChildren.size();
RemoteNode[] children = new RemoteNode[len];
int numNodes = 1;
int maxChildHeight = -1;
int numLeaves = len == 0 ? 1 : 0;
int leftIndex = nextTraversalIndex;
nextTraversalIndex++;
for (int i = 0; i < len; i++) {
Node myChild = myChildren.get(i);
RemoteNode child = convertDataModels(myChild, depth + 1, nextTraversalIndex);
children[i] = child;
//note: numNodes, height and numLeaves are fields in RemoteNode, so the tree is not actually traversed again for each of these.
maxChildHeight = Math.max(maxChildHeight, child.findMaximumDepthToLeaf());
numLeaves += child.getNumberOfLeafNodes();
numNodes += child.getNumberOfNodes();
nextTraversalIndex = child.getRightIndex() + 1;
}
//create a RemoteNode for the current node
String label = node.getName();
int height = maxChildHeight + 1;
int id = -1; //id will be assigned when this node is inserted in the DB
int numChildren = children.length;
RemoteNode rNode = new RemoteNode(id, label, numChildren, numNodes, numLeaves, depth, height, leftIndex, nextTraversalIndex);
rNode.setChildren(children);
return rNode;
}
@Override
public int importFromNewick(String newick, String name)
{
RemoteNode root = rootNodeFromNewick(newick, name);
return this.importTreeData(root, name);
}
}