package net.dirtyfilthy.Bitten;
import java.awt.event.KeyEvent;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.assignment.DataSizeAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.action.layout.graph.NodeLinkTreeLayout;
import prefuse.activity.Activity;
import prefuse.controls.ControlAdapter;
import prefuse.controls.DragControl;
import prefuse.controls.FocusControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Graph;
import prefuse.data.Table;
import prefuse.data.event.EventConstants;
import prefuse.data.io.DataIOException;
import prefuse.data.io.sql.ConnectionFactory;
import prefuse.data.io.sql.DatabaseDataSource;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.force.DragForce;
import prefuse.util.force.EulerIntegrator;
import prefuse.visual.VisualItem;
public class TransactionView extends Display {
private DatabaseDataSource dbSource;
private String label="label";
private Table transactionNodeTable=new Table();
private Table transactionEdgeTable=new Table();
private Graph graph;
private Visualization viz;
private Connection db;
public TransactionView(Connection c) throws SQLException{
db=c;
int[] palette = new int[] {
ColorLib.rgb(255,180,180), ColorLib.rgb(190,190,255), ColorLib.rgb(190,190,0)
};
dbSource=ConnectionFactory.getDatabaseConnection(c);
transactionNodeTable.addColumn("id",int.class);
transactionNodeTable.addColumn("type",int.class);
transactionNodeTable.addColumn("transaction_id",int.class);
transactionNodeTable.addColumn("btc",double.class);
transactionEdgeTable.addColumn("id", int.class);
transactionEdgeTable.addColumn("source", int.class);
transactionEdgeTable.addColumn("target", int.class);
transactionEdgeTable.addColumn("btc",double.class);
transactionEdgeTable.addColumn("type",int.class);
graph=new Graph(transactionNodeTable, transactionEdgeTable, true, "id", "source","target");
viz=new Visualization();
viz.add("graph", graph);
// draw the "name" label for NodeItems
LabelRenderer r = new LabelRenderer("id");
r.setRoundedCorner(8, 8); // round the corners
EdgeRenderer e = new EdgeRenderer();
e.setArrowType(Constants.EDGE_ARROW_FORWARD);
e.setArrowHeadSize(8, 8);
FocusControl focusControl=new FocusControl(2){
public void itemClicked(VisualItem i, java.awt.event.MouseEvent e){
int id=i.getInt("id");
int type=i.getInt("type");
int transaction_id=i.getInt("transaction_id");
if(type==2){
expandOutput(id,false);
}
else if(type==0 || type==1){
expandTransaction(transaction_id);
}
}
};
ControlAdapter keyboardAdapter=new ControlAdapter(){
public void keyTyped(KeyEvent e){
processKeys(e);
}
private void processKeys(KeyEvent e){
switch(e.getKeyChar()){
case 's':
showSearchDialog();
break;
}
}
};
// create a new default renderer factory
// return our name label renderer as the default for all non-EdgeItems
// includes straight line edges for EdgeItems by default
viz.setRendererFactory(new DefaultRendererFactory(r,e));
ActionList layout = new ActionList(Activity.INFINITY);
ForceDirectedLayout f=new ForceDirectedLayout("graph");
// f.getForceSimulator().setIntegrator(new EulerIntegrator());
f.getForceSimulator().addForce(new DragForce((float) 0.01));
layout.add(f);
//layout.add(new NodeLinkTreeLayout("graph"));
layout.add(new RepaintAction());
DataColorAction fill = new DataColorAction("graph.nodes", "type",
Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
ColorAction text = new ColorAction("graph.nodes",
VisualItem.TEXTCOLOR, ColorLib.gray(0));
// use light grey for edges
ColorAction edges = new ColorAction("graph.edges",
VisualItem.STROKECOLOR, ColorLib.gray(200));
ColorAction fill2 = new ColorAction("graph.edges",
VisualItem.FILLCOLOR, ColorLib.gray(200));
DataSizeAction edgeWidth = new DataSizeAction("graph.edges", "btc");
DataColorAction edgeColor = new DataColorAction("graph.edges", "type", Constants.NUMERICAL, VisualItem.STROKECOLOR,new int[] {ColorLib.rgb(255,0,0),ColorLib.rgb(0,255,0)});
edgeColor.setBinCount(2);
edgeWidth.setScale(Constants.LOG_SCALE);
edgeWidth.setMaximumSize(20);
ActionList color = new ActionList();
color.add(text);
color.add(edgeColor);
color.add(fill);
color.add(fill2);
color.add(edgeWidth);
viz.putAction("layout", layout);
viz.putAction("color", color);
setSize(720, 500); // set display size
setVisualization(viz);
addControlListener(new DragControl()); // drag items around
addControlListener(new PanControl()); // pan with background left-drag
addControlListener(new ZoomControl()); // zoom with vertical right-drag
addControlListener(focusControl);
addControlListener(keyboardAdapter);
viz.run("color");
// start up the animated layout
viz.run("layout");
}
public void showSearchDialog(){
String s = (String)JOptionPane.showInputDialog(this, "Search:","Search", JOptionPane.QUESTION_MESSAGE);
expandStringAddress(s, true);
}
public void expandTransaction(final long transactionId){
System.out.println("expanding tran "+transactionId);
Runnable r=new Runnable() {
public void run(){
try {
ArrayList<Long> a=new ArrayList<Long>();
PreparedStatement s=db.prepareStatement("SELECT id FROM transaction_outputs WHERE transaction_id=? UNION SELECT previous_output_id as id FROM transaction_inputs WHERE transaction_id=? and previous_output_id!=0");
s.setLong(1, transactionId);
s.setLong(2, transactionId);
ResultSet rs=s.executeQuery();
while(rs.next()){
a.add(rs.getLong(1));
}
rs.close();
expandOutputMulti((Long[]) a.toArray(new Long[a.size()]),true);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
new Thread(r).start();
}
public void expandOutputMulti(final Long[] idz, boolean sync){
System.out.println("expanding out "+idz);
Runnable r=new Runnable() {
public void run(){
try {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < idz.length;) {
builder.append(idz[i]);
if (++i < idz.length) {
builder.append(",");
}
}
ArrayList<Long> a=new ArrayList<Long>();
PreparedStatement s=db.prepareStatement("SELECT DISTINCT (transaction_id) as id from transaction_outputs where id IN ("+builder+") UNION SELECT DISTINCT (transaction_id) as id from transaction_inputs where previous_output_id IN ("+builder+")");
System.out.println("execute query");
ResultSet rs=s.executeQuery();
while(rs.next()){
a.add(rs.getLong(1));
}
rs.close();
loadTransaction((Long[]) a.toArray(new Long[a.size()]));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
if(sync){
r.run();
}
else{
new Thread(r).start();
}
}
public void expandStringAddress(final String address, boolean sync){
Runnable r=new Runnable() {
public void run(){
try {
ArrayList<Long> a=new ArrayList<Long>();
PreparedStatement s=db.prepareStatement("SELECT DISTINCT (transaction_id) from addresses join transaction_outputs ON to_address_id=addresses.id where addresses.base58hash=? UNION SELECT DISTINCT (transaction_id) from addresses join transaction_inputs ON from_address_id=addresses.id where addresses.base58hash=?");
s.setString(1, address.trim());
s.setString(2, address.trim());
System.out.println("execute query find address "+address);
ResultSet rs=s.executeQuery();
System.out.println("after exec");
while(rs.next()){
a.add(rs.getLong(1));
}
rs.close();
System.out.println("load transaction");
loadTransaction((Long[]) a.toArray(new Long[a.size()]));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
if(sync){
r.run();
}
else{
new Thread(r).start();
}
}
public void expandOutput(final long outputId, boolean sync){
System.out.println("expanding out "+outputId);
Runnable r=new Runnable() {
public void run(){
try {
ArrayList<Long> a=new ArrayList<Long>();
PreparedStatement s=db.prepareStatement("SELECT DISTINCT (transactions.id) from transactions left join transaction_inputs ON transactions.id=transaction_inputs.transaction_id LEFT JOIN transaction_outputs ON transactions.id=transaction_outputs.transaction_id WHERE transaction_outputs.id=? OR transaction_inputs.previous_output_id=?");
s.setLong(1, outputId);
s.setLong(2, outputId);
System.out.println("execute query");
ResultSet rs=s.executeQuery();
while(rs.next()){
a.add(rs.getLong(1));
}
rs.close();
System.out.println("load transaction");
loadTransaction((Long[]) a.toArray(new Long[a.size()]));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
if(sync){
r.run();
}
else{
new Thread(r).start();
}
}
public void loadTransaction(Long[] idz){
String sql="";
StringBuilder builder = new StringBuilder();
for (int i = 0; i < idz.length;) {
builder.append(idz[i]);
if (++i < idz.length) {
builder.append(",");
}
}
synchronized(viz){
try {
sql="SELECT (id+9999999) AS id, is_coinbase AS type, id as transaction_id, 0.0 AS btc FROM transactions WHERE id IN("+builder+")";
dbSource.getData(transactionNodeTable,sql,"id");
sql="SELECT previous_output_id AS id,2 AS type, transaction_inputs.transaction_id, value/100000000.0 AS btc FROM transaction_inputs LEFT JOIN transaction_outputs ON transaction_outputs.id=previous_output_id WHERE previous_output_id!=0 AND transaction_inputs.transaction_id IN("+builder+")";
dbSource.getData(transactionNodeTable,sql,"id");
sql="SELECT id AS id,2 AS type, transaction_id,value/100000000.0 AS btc FROM transaction_outputs WHERE transaction_id IN("+builder+")";
dbSource.getData(transactionNodeTable,sql,"id");
sql="SELECT transaction_inputs.id as id, (transaction_inputs.transaction_id+9999999) as target, previous_output_id as source, value/100000000.0 AS btc, 0 AS type FROM transaction_inputs LEFT JOIN transaction_outputs ON transaction_outputs.id=previous_output_id WHERE previous_output_id!=0 AND transaction_inputs.transaction_id IN("+builder+")";
dbSource.getData(transactionEdgeTable,sql,"id");
sql="SELECT (id+9999999) as id, (transaction_id+9999999) as source, id as target, value/100000000.0 AS btc, 1 as type FROM transaction_outputs WHERE transaction_id IN("+builder+")";
dbSource.getData(transactionEdgeTable,sql,"id");
viz.run("color");
} catch (DataIOException e) {
throw new RuntimeException(e);
}
}
}
}